Commit 4c1e3c97 authored by David Sveningsson's avatar David Sveningsson
Browse files

feat(meta): add new list property to `MetaAttribute`

When enabled the attribute value is parsed as a token list where each token is
validated individually.
parent 6132b829
Pipeline #377489055 passed with stages
in 12 minutes and 15 seconds
......@@ -197,6 +197,7 @@ export interface MetaAttribute {
boolean?: boolean;
deprecated?: boolean | string;
enum?: Array<string | RegExp>;
list?: boolean;
required?: boolean;
omit?: boolean;
......@@ -221,6 +222,8 @@ The value cannot be omitted or be used as a boolean property.
This is used by the {@link attribute-allowed-values} rule.
An empty object may be set as well to mark the attribute as a known attribute but without any validation.
#### `attribute.enum`
The `enum` property is a list of allowed values the attribute can have.
......@@ -256,6 +259,27 @@ If set to `true` this attribute is required to be present on the element.
This is used by the [element-required-attributes](/rules/element-required-attributes.html) rule.
#### `attribute.list`
If set to `true` the attribute value is parsed as a space-separated list (`DOMTokenList`) where each token is validated separately and each token must be valid for the attribute value to be consideted valid.
"custom-element": {
"attributes": {
"foo": {
"list": true,
"enum": ["a", "b"]
Given the metadata above both `foo="a"` and `foo="b"` is valid.
When the attribute is `foo="a b"` each token (`a` and `b`) is validated separately and both must be valid.
Thus `foo="a b"` is valid but `foo="a c"` is not.
#### Deprecated method
The previous (now deprecated) method was to assign an enumerated list of valid values:
......@@ -38,6 +38,10 @@ export interface MetaAttribute {
* this attribute can have (each token if list is set) */
enum?: Array<string | RegExp>;
/* if true this attribute contains space-separated tokens and each token must
* be valid by itself */
list?: boolean;
/* if true this attribute can omit the value */
omit?: boolean;
......@@ -685,6 +685,24 @@ describe("Meta validator", () => {
expect(Validator.validateAttribute(self, rules)).toBeTruthy();
expect(Validator.validateAttribute(other, rules)).toBeFalsy();
value | expected
${"foo"} | ${true}
${"bar"} | ${true}
${"baz"} | ${false}
${"foo bar"} | ${true}
${"foo baz"} | ${false}
${"foo bar"} | ${true}
${"foo baz"} | ${false}
`('should validate each token when using list: "$value"', ({ value, expected }) => {
const rules: Record<string, MetaAttribute> = {
foo: { list: true, enum: ["foo", "bar"] },
const attr = new Attribute("foo", value, location, location);
expect(Validator.validateAttribute(attr, rules)).toBe(expected);
import { Attribute, DynamicValue, HtmlElement } from "../dom";
import { Attribute, DynamicValue, HtmlElement, DOMTokenList } from "../dom";
import {
......@@ -188,6 +188,18 @@ export class Validator {
return true;
/* validate each token when using list, all tokens must be valid */
if (rule.list) {
const tokens = new DOMTokenList(value, attr.valueLocation);
return tokens.every((token) => {
return this.validateAttributeValue(token, rule);
return this.validateAttributeValue(value, rule);
private static validateAttributeValue(value: string | null, rule: MetaAttribute): boolean {
/* skip attribute if it not have enumerated list */
if (!rule.enum) {
return true;
......@@ -257,6 +257,10 @@
"title": "Set to true or string if this attribute is deprecated",
"oneOf": [{ "type": "boolean" }, { "type": "string" }]
"list": {
"type": "boolean",
"title": "Set to true if this attribute is a list of space-separated tokens, each which must be valid by itself"
"enum": {
"type": "array",
"title": "Exhaustive list of values (string or regex) this attribute accepts",
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment