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

Add new config schema allOf condition support #4791

Open
wants to merge 12 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
Original file line number Diff line number Diff line change
Expand Up @@ -954,11 +954,11 @@
i18n: {
showAllSections: 'Dangos adrannau',
hideAllSections: 'Cuddio adrannau',
showSection: 'Dangos',
showSectionAriaLabel: 'Dangos adran',
hideSection: 'Cuddio',
hideSectionAriaLabel: 'Cuddio adran'
},
'i18n.showSection': 'Dangos',
'i18n.showSectionAriaLabel': 'Dangos adran',
'i18n.hideSection': 'Cuddio',
'i18n.hideSectionAriaLabel': 'Cuddio adran'
}
})
</script>
Expand Down
334 changes: 308 additions & 26 deletions packages/govuk-frontend/src/govuk/common/index.jsdom.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,35 @@ describe('Common JS utilities', () => {
}
}

it('flattens a single object', () => {
it('ignores a single object', () => {
const config = mergeConfigs(config1)
expect(config).toEqual({
a: 'antelope',
'c.a': 'camel'
c: { a: 'camel' }
})
})

it('flattens and merges two objects', () => {
it('merges two objects', () => {
const config = mergeConfigs(config1, config2)
expect(config).toEqual({
a: 'aardvark',
b: 'bee',
'c.a': 'cat',
'c.o': 'cobra'
c: { a: 'cat', o: 'cobra' }
})
})

it('flattens and merges three objects', () => {
it('merges three objects', () => {
const config = mergeConfigs(config1, config2, config3)
expect(config).toEqual({
a: 'aardvark',
b: 'bat',
'c.a': 'cat',
'c.o': 'cow',
c: { a: 'cat', o: 'cow' },
d: 'dog',
'e.l.e': 'elephant'
e: {
l: {
e: 'elephant'
}
}
})
})

Expand All @@ -75,10 +77,29 @@ describe('Common JS utilities', () => {
expect(config).toEqual({
a: 'antelope',
b: 'bat',
'c.a': 'camel',
'c.o': 'cow',
c: { a: 'camel', o: 'cow' },
d: 'dog',
e: {
l: {
e: 'elephant'
}
}
})
})

it('prioritises the last parameter provided (different types)', () => {
const config = mergeConfigs(config1, config2, config3, {
c: 'jellyfish', // Replaces top-level object with string
e: { l: 'shark' } // Replaces nested object with string
})
expect(config).toEqual({
a: 'aardvark',
b: 'bat',
c: 'jellyfish',
d: 'dog',
'e.l.e': 'elephant'
e: {
l: 'shark'
}
})
})

Expand All @@ -89,26 +110,283 @@ describe('Common JS utilities', () => {
})

describe('extractConfigByNamespace', () => {
const flattenedConfig = {
a: 'aardvark',
'b.a': 'bat',
'b.e': 'bear',
'b.o': 'boar',
'c.a': 'camel',
'c.o': 'cow',
d: 'dog',
e: 'elephant'
class Component {
/**
* @satisfies {Schema}
*/
static schema = {
properties: {
a: { type: 'string' },
b: { type: 'object' },
c: { type: 'object' },
d: { type: 'string' },
e: { type: 'string' },
f: { type: 'object' }
}
}
}

it('can extract single key-value pairs', () => {
const result = extractConfigByNamespace(flattenedConfig, 'a')
expect(result).toEqual({ a: 'aardvark' })
/** @type {HTMLElement} */
let $element

beforeEach(() => {
document.body.outerHTML = outdent`
<div id="app-example"
data-a="aardvark"
data-b.a="bat"
data-b.e="bear"
data-b.o="boar"
data-c.a="camel"
data-c.o="cow"
data-d="dog"
data-e="element">
</div>
`

$element = document.getElementById('app-example')
})

it('can extract multiple key-value pairs', () => {
const result = extractConfigByNamespace(flattenedConfig, 'b')
it('defaults to empty config for known namespaces only', () => {
const { dataset } = $element

const nonObject1 = extractConfigByNamespace(Component, dataset, 'a')
const nonObject2 = extractConfigByNamespace(Component, dataset, 'd')
const nonObject3 = extractConfigByNamespace(Component, dataset, 'e')

const namespaceKnown = extractConfigByNamespace(Component, dataset, 'f')
const namespaceUnknown = extractConfigByNamespace(
Component,
dataset,
'unknown'
)

// With known namespace but non-object type, default to no config
expect(nonObject1).toEqual(undefined)
expect(nonObject2).toEqual(undefined)
expect(nonObject3).toEqual(undefined)

// With known namespace, default to empty config
expect(namespaceKnown).toEqual({})

// With unknown namespace, default to no config
expect(namespaceUnknown).toEqual(undefined)
})

it('can extract config from key-value pairs', () => {
const result = extractConfigByNamespace(Component, $element.dataset, 'b')
expect(result).toEqual({ a: 'bat', e: 'bear', o: 'boar' })
})

it('can extract config from key-value pairs (with invalid namespace, first)', () => {
document.body.outerHTML = outdent`
<div id="app-example2"
data-i18n
data-i18n.key1="One"
data-i18n.key2="Two"
data-i18n.key3="Three">
</div>
`

const { dataset } = document.getElementById('app-example2')
const result = extractConfigByNamespace(
class Component {
/**
* @satisfies {Schema}
*/
static schema = {
properties: {
i18n: { type: 'object' }
}
}
},
dataset,
'i18n'
)

expect(result).toEqual({ key1: 'One', key2: 'Two', key3: 'Three' })
})

it('can extract config from key-value pairs (with invalid namespace, last)', () => {
document.body.outerHTML = outdent`
<div id="app-example2"
data-i18n.key1="One"
data-i18n.key2="Two"
data-i18n.key3="Three"
data-i18n>
</div>
`

const { dataset } = document.getElementById('app-example2')
const result = extractConfigByNamespace(
class Component {
/**
* @satisfies {Schema}
*/
static schema = {
properties: {
i18n: { type: 'object' }
}
}
},
dataset,
'i18n'
)

expect(result).toEqual({ key1: 'One', key2: 'Two', key3: 'Three' })
})

it('handles when both shallow and deep keys are set (namespace collision)', () => {
document.body.outerHTML = outdent`
<div id="app-example"
data-a="aardvark"
data-b="bat"
data-c="jellyfish"
data-c.a="cat"
data-c.o="cow"
data-d="dog"
data-e="element"
data-f.e="elk"
data-f.e.l="elephant">
</div>
`

const { dataset } = document.getElementById('app-example')
const result = extractConfigByNamespace(Component, dataset, 'c')

expect(result).toEqual({ a: 'cat', o: 'cow' })
})

it('handles when both shallow and deep keys are set (namespace collision + key collision in namespace)', () => {
document.body.outerHTML = outdent`
<div id="app-example"
data-a="aardvark"
data-b="bat"
data-c.c="crow"
data-c="jellyfish"
data-c.a="cat"
data-c.o="cow"
data-d="dog"
data-e="element"
data-f.e="elk"
data-f.e.l="elephant">
</div>
`

const { dataset } = document.getElementById('app-example')
const result = extractConfigByNamespace(Component, dataset, 'c')

expect(result).toEqual({ a: 'cat', c: 'crow', o: 'cow' })
})

it('handles when both shallow and deep keys are set (namespace collision + key collision in namespace after shallow)', () => {
document.body.outerHTML = outdent`
<div id="app-example"
data-a="aardvark"
data-b="bat"
data-c="jellyfish"
data-c.a="cat"
data-c.c="crow"
data-c.o="cow"
data-d="dog"
data-e="element"
data-f.e="elk"
data-f.e.l="elephant">
</div>
`

const { dataset } = document.getElementById('app-example')
const result = extractConfigByNamespace(Component, dataset, 'c')

expect(result).toEqual({ a: 'cat', c: 'crow', o: 'cow' })
})

it('handles when both shallow and deep keys are set (deeper collision)', () => {
document.body.outerHTML = outdent`
<div id="app-example"
data-a="aardvark"
data-b="bat"
data-c="jellyfish"
data-c.a="cat"
data-c.o="cow"
data-d="dog"
data-f.e="elk"
data-f.e.l="elephant">
</div>
`

const { dataset } = document.getElementById('app-example')
const result = extractConfigByNamespace(Component, dataset, 'f')

expect(result).toEqual({ e: { l: 'elephant' } })
})

it('can handle multiple levels of nesting', () => {
document.body.outerHTML = outdent`
<div id="app-example2"
data-i18n.key1="This, That"
data-i18n.key2.one="The"
data-i18n.key2.other="Other">
</div>
`

const { dataset } = document.getElementById('app-example2')
const result = extractConfigByNamespace(
class Component {
/**
* @satisfies {Schema}
*/
static schema = {
properties: {
i18n: { type: 'object' }
}
}
},
dataset,
'i18n'
)

expect(result).toEqual({
key1: 'This, That',
key2: {
one: 'The',
other: 'Other'
}
})
})

it('can handle multiple levels of nesting (prioritises the last parameter provided)', () => {
document.body.outerHTML = outdent`
<div id="app-example2"
data-i18n.key1.one="This"
data-i18n.key1.other="That"
data-i18n.key2.one="The"
data-i18n.key2.other="Other"
data-i18n.key1="This, That"
data-i18n.key2="The Other">
</div>
`

const { dataset } = document.getElementById('app-example2')
const result = extractConfigByNamespace(
class Component {
/**
* @satisfies {Schema}
*/
static schema = {
properties: {
i18n: { type: 'object' }
}
}
},
dataset,
'i18n'
)

expect(result).toEqual({
key1: 'This, That',
key2: 'The Other'
})
})
})

describe('isSupported', () => {
Expand Down Expand Up @@ -219,3 +497,7 @@ describe('Common JS utilities', () => {
})
})
})

/**
* @typedef {import('./index.mjs').Schema} Schema
*/
Loading