Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTML Boolean #164

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
- `options.props` - Array of camelCasedProps to watch as String values or { [camelCasedProps]: "string" | "number" | "boolean" | "function" | "json" }

- When specifying Array or Object as the type, the string passed into the attribute must pass `JSON.parse()` requirements.
- When specifying Boolean as the type, "true", "1", "yes", "TRUE", and "t" are mapped to `true`. All strings NOT begining with t, T, 1, y, or Y will be `false`.
- When specifying Boolean as the type, "true", "1"…"9", "yes", "TRUE", and "t", as well as absence of a value, the empty string, and value equal to the name of attribute are mapped to `true`. All strings NOT begining with t, T, 1…9, y, or Y but for the name of attribute will be `false`.
- When specifying Function as the type, the string passed into the attribute must be the name of a function on `window` (or `global`). The `this` context of the function will be the instance of the WebComponent / HTMLElement when called.
- If PropTypes are defined on the React component, the `options.props` will be ignored and the PropTypes will be used instead.
However, we strongly recommend using `options.props` instead of PropTypes as it is usually not a good idea to use PropTypes in production.
Expand Down Expand Up @@ -127,7 +127,11 @@ customElements.define(
numProp: "number",
floatProp: "number",
trueProp: "boolean",
htmlTruePropPresent: "boolean",
htmlTruePropEmpty: "boolean",
htmlTruePropSame: "boolean",
falseProp: "boolean",
htmlFalsePropAbsent: "boolean",
arrayProp: "json",
objProp: "json",
},
Expand All @@ -140,10 +144,14 @@ document.body.innerHTML = `
num-prop="360"
float-prop="0.5"
true-prop="true"
html-true-prop-present
html-true-prop-empty=""
html-true-prop-same="html-true-prop-same"
false-prop="false"
array-prop='[true, 100.25, "👽", { "aliens": "welcome" }]'
obj-prop='{ "very": "object", "such": "wow!" }'
></attr-prop-type-casting>
<!-- note the lack of html-false-prop-absent -->
`

/*
Expand All @@ -153,7 +161,11 @@ document.body.innerHTML = `
numProp: 360,
floatProp: 0.5,
trueProp: true,
htmlTruePropPresent: true,
htmlTruePropEmpty: true,
htmlTruePropSame: true,
falseProp: false,
htmlFalsePropAbsent: false,
arrayProp: [true, 100.25, "👽", { aliens: "welcome" }],
objProp: { very: "object", such: "wow!" },
}
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/core.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ describe("core", () => {
text: string
numProp: number
boolProp: boolean
htmlBoolProp: boolean
arrProp: string[]
objProp: { [key: string]: string }
funcProp: () => void
Expand All @@ -118,6 +119,7 @@ describe("core", () => {
text,
numProp,
boolProp,
htmlBoolProp,
arrProp,
objProp,
funcProp,
Expand All @@ -132,6 +134,7 @@ describe("core", () => {
text: "string",
numProp: "number",
boolProp: "boolean",
htmlBoolProp: "boolean",
arrProp: "json",
objProp: "json",
funcProp: "function",
Expand All @@ -154,7 +157,7 @@ describe("core", () => {
customElements.define("test-button-element-property", ButtonElement)

const body = document.body
body.innerHTML = `<test-button-element-property text='hello' obj-prop='{"greeting": "hello, world"}' arr-prop='["hello", "world"]' num-prop='240' bool-prop='true' func-prop='globalFn'>
body.innerHTML = `<test-button-element-property text='hello' obj-prop='{"greeting": "hello, world"}' arr-prop='["hello", "world"]' num-prop='240' bool-prop='true' html-bool-prop func-prop='globalFn'>
</test-button-element-property>`

const element = body.querySelector(
Expand All @@ -166,6 +169,7 @@ describe("core", () => {
expect(element.text).toBe("hello")
expect(element.numProp).toBe(240)
expect(element.boolProp).toBe(true)
expect(element.htmlBoolProp).toBe(true)
expect(element.arrProp).toEqual(["hello", "world"])
expect(element.objProp).toEqual({ greeting: "hello, world" })
expect(element.funcProp).toBeInstanceOf(Function)
Expand All @@ -174,14 +178,16 @@ describe("core", () => {
element.text = "world"
element.numProp = 100
element.boolProp = false
element.htmlBoolProp = false
//@ts-ignore
element.funcProp = global.newFunc

await wait()

expect(element.getAttribute("text")).toBe("world")
expect(element.getAttribute("num-prop")).toBe("100")
expect(element.getAttribute("bool-prop")).toBe("false")
expect(element).not.toHaveAttribute("bool-prop")
expect(element).not.toHaveAttribute("html-bool-prop")
expect(element.getAttribute("func-prop")).toBe("newFunc")
})

Expand Down
20 changes: 16 additions & 4 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ export default function r2wc<Props extends R2WCBaseProps, Context>(
const type = propTypes[prop]
const transform = type ? transforms[type] : null

if (transform?.parse && value) {
if (!value && type === "boolean") {
//@ts-ignore
this[propsSymbol][prop] = this.hasAttribute(attribute)
} else if (transform?.parse && value) {
//@ts-ignore
this[propsSymbol][prop] = transform.parse(value, attribute, this)
}
Expand Down Expand Up @@ -125,7 +128,12 @@ export default function r2wc<Props extends R2WCBaseProps, Context>(
const type = propTypes[prop]
const transform = type ? transforms[type] : null

if (prop in propTypes && transform?.parse && value) {
if (!value && type === "boolean") {
//@ts-ignore
this[propsSymbol][prop] = this.hasAttribute(attribute)

this[renderSymbol]()
} else if (prop in propTypes && transform?.parse && value) {
//@ts-ignore
this[propsSymbol][prop] = transform.parse(value, attribute, this)

Expand Down Expand Up @@ -159,10 +167,14 @@ export default function r2wc<Props extends R2WCBaseProps, Context>(
return this[propsSymbol][prop]
},
set(value) {
const oldValue = this[propsSymbol][prop]
const transform = type ? transforms[type] : null

this[propsSymbol][prop] = value

const transform = type ? transforms[type] : null
if (transform?.stringify) {
if (type === "boolean" && !value && oldValue) {
this.removeAttribute(attribute)
} else if (transform?.stringify) {
//@ts-ignore
const attributeValue = transform.stringify(value, attribute, this)
const oldAttributeValue = this.getAttribute(attribute)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/transforms/boolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Transform } from "./index"

const boolean: Transform<boolean> = {
stringify: (value) => (value ? "true" : "false"),
parse: (value) => /^[ty1-9]/i.test(value),
parse: (value, attribute) => value === attribute || /^[ty1-9]/i.test(value),
}

export default boolean
Loading