-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
658 additions
and
2 deletions.
There are no files selected for viewing
251 changes: 251 additions & 0 deletions
251
packages/components/src/components/expandable/expandable.stories.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
import '../../solid-components'; | ||
import { html } from 'lit'; | ||
import { storybookDefaults, storybookHelpers, storybookTemplate } from '../../../scripts/storybook/helper'; | ||
import { userEvent } from '@storybook/test'; | ||
import { waitUntil } from '@open-wc/testing-helpers'; | ||
|
||
const { argTypes, parameters } = storybookDefaults('sd-expandable'); | ||
const { overrideArgs } = storybookHelpers('sd-expandable'); | ||
const { generateTemplate } = storybookTemplate('sd-expandable'); | ||
|
||
export default { | ||
title: 'Components/sd-expandable', | ||
component: 'sd-expandable', | ||
args: overrideArgs([ | ||
{ type: 'slot', name: 'default', value: '<div class="slot slot--border slot--text h-16">Default slot</div>' } | ||
]), | ||
argTypes, | ||
parameters: { ...parameters } | ||
}; | ||
|
||
/** | ||
* Expandable shows a brief summary and expands to show additional content. | ||
*/ | ||
export const Default = { | ||
render: (args: any) => { | ||
return generateTemplate({ | ||
args | ||
}); | ||
} | ||
}; | ||
|
||
/** | ||
* Use the inverted attribute to make an expandable with inverted colors. | ||
*/ | ||
export const Inverted = { | ||
parameters: { controls: { exclude: 'inverted' } }, | ||
render: (args: any) => { | ||
return generateTemplate({ | ||
axis: { | ||
y: { type: 'attribute', name: 'inverted' } | ||
}, | ||
args, | ||
options: { | ||
templateBackgrounds: { alternate: 'y', colors: ['rgb(var(--sd-color-primary, 0 53 142))', 'white'] } | ||
} | ||
}); | ||
} | ||
}; | ||
|
||
/** | ||
* Use the `default`, `toggle-open` and `toggle-closed` slots to add content to the expandable. | ||
*/ | ||
export const Slots = { | ||
parameters: { | ||
controls: { exclude: ['default', 'toggle-open', 'toggle-closed'] } | ||
}, | ||
render: (args: any) => { | ||
return html` | ||
${['default', 'toggle-open', 'toggle-closed'].map(slot => | ||
generateTemplate({ | ||
axis: { | ||
x: { | ||
type: 'slot', | ||
name: slot, | ||
title: 'slot=...', | ||
values: [ | ||
{ | ||
value: | ||
slot === 'default' | ||
? `<div class="slot slot--border slot--background slot--text h-full">Default slot</div>` | ||
: `<div slot='${slot}' class="slot slot--border slot--background slot--text h-12"></div>`, | ||
title: slot | ||
} | ||
] | ||
} | ||
}, | ||
args | ||
}) | ||
)} | ||
`; | ||
} | ||
}; | ||
|
||
/** | ||
* Use the `content`, `toggle`, `summary` and `details` parts to style the expandable. | ||
*/ | ||
export const Parts = { | ||
parameters: { | ||
controls: { | ||
exclude: ['open', 'content', 'toggle', 'summary', 'details'] | ||
} | ||
}, | ||
render: (args: any) => { | ||
return generateTemplate({ | ||
axis: { | ||
y: { | ||
type: 'template', | ||
name: 'sd-expandable::part(...){outline: solid 2px red}', | ||
values: ['content', 'toggle', 'summary', 'details'].map(part => { | ||
return { | ||
title: part, | ||
value: `<style>#part-${part} sd-expandable::part(${part}){outline: solid 2px red; outline-offset: -2px;}</style><div id="part-${part}">%TEMPLATE%</div>` | ||
}; | ||
}) | ||
} | ||
}, | ||
args | ||
}); | ||
} | ||
}; | ||
|
||
/** | ||
* sd-expandable is fully accessibile via keyboard. | ||
*/ | ||
|
||
export const Mouseless = { | ||
render: (args: any) => { | ||
return html`<div class="mouseless">${generateTemplate({ args })}</div>`; | ||
}, | ||
|
||
play: async ({ canvasElement }: { canvasElement: HTMLUnknownElement }) => { | ||
const el = canvasElement.querySelector('.mouseless sd-expandable'); | ||
await waitUntil(() => el?.shadowRoot?.querySelector('button')); | ||
await userEvent.type(el!.shadowRoot!.querySelector('button')!, '{return}', { pointerEventsCheck: 0 }); | ||
} | ||
}; | ||
|
||
/** | ||
* Expandable can be used with background options of white, neutral-100 and primary-100. When using these options, use the `--gradient-color-start` and `--gradient-color-end` CSS variables to align the gradient colors. | ||
* | ||
* The inverted attribute can be used when the background is primary. The default slot can be used with 2 variants for alternate expandable experiences: lead text and paragraph. | ||
*/ | ||
export const Samples = { | ||
render: (args: any) => { | ||
return html` | ||
<style> | ||
.background-sample { | ||
padding: 16px; | ||
margin-bottom: 32px; | ||
width: 100%; | ||
box-sizing: border-box; | ||
} | ||
</style> | ||
<div class="p-4 mb-8 bg-neutral-100 text-left text-[14px] font-bold box-border">Background white</div> | ||
<div class="background-sample"> | ||
${generateTemplate({ | ||
args: { ...args } | ||
})} | ||
</div> | ||
<div class="w-full p-4 mb-8 bg-neutral-100 text-left text-[14px] font-bold box-border"> | ||
Background neutral-100 | ||
</div> | ||
<div class="background-sample bg-neutral-100"> | ||
${generateTemplate({ | ||
args: { ...args }, | ||
constants: [ | ||
{ | ||
type: 'cssProperty', | ||
name: '--gradient-color-start', | ||
value: 'rgba(246, 246, 246, 0)' | ||
}, | ||
{ | ||
type: 'cssProperty', | ||
name: '--gradient-color-end', | ||
value: 'rgba(246, 246, 246, 1)' | ||
} | ||
] | ||
})} | ||
</div> | ||
<div class="w-full p-4 mb-8 bg-neutral-100 text-left text-[14px] font-bold box-border"> | ||
Background primary-100 | ||
</div> | ||
<div class="background-sample bg-primary-100"> | ||
${generateTemplate({ | ||
args: { ...args }, | ||
constants: [ | ||
{ | ||
type: 'cssProperty', | ||
name: '--gradient-color-start', | ||
value: 'rgba(236, 240, 249, 0)' | ||
}, | ||
{ | ||
type: 'cssProperty', | ||
name: '--gradient-color-end', | ||
value: 'rgba(236, 240, 249, 1)' | ||
} | ||
] | ||
})} | ||
</div> | ||
<div class="w-full p-4 mb-8 bg-neutral-100 text-left text-[14px] font-bold box-border"> | ||
Background primary, inverted | ||
</div> | ||
<div class="background-sample bg-primary"> | ||
${generateTemplate({ | ||
args: { ...args, inverted: true } | ||
})} | ||
</div> | ||
<div class="w-full p-4 mb-8 bg-neutral-100 text-left text-[14px] font-bold box-border">Lead Text Example</div> | ||
<div class="background-sample bg-neutral-100 "> | ||
${generateTemplate({ | ||
args: overrideArgs([ | ||
{ | ||
type: 'slot', | ||
name: 'default', | ||
value: | ||
'<div class="sd-leadtext">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nibh justo ullamcorper odio tempor molestie phasellus dui vel id. Velit in sed non orci pellentesque vivamus nunc. At non tortor, sit neque tristique. Facilisis commodo integer hendrerit tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nibh justo ullamcorper odio tempor molestie phasellus dui vel id. Velit in sed non orci pellentesque vivamus nunc. At non tortor, sit neque tristique. Facilisis commodo integer hendrerit tortor.</div>' | ||
}, | ||
{ | ||
type: 'attribute', | ||
name: 'variant', | ||
value: 'leadtext' | ||
}, | ||
{ | ||
type: 'cssProperty', | ||
name: '--gradient-color-start', | ||
value: 'rgba(246, 246, 246, 0)' | ||
}, | ||
{ | ||
type: 'cssProperty', | ||
name: '--gradient-color-end', | ||
value: 'rgba(246, 246, 246, 1)' | ||
} | ||
]) | ||
})} | ||
</div> | ||
<div class="w-full p-4 mb-8 bg-neutral-100 text-left text-[14px] font-bold box-border">Paragraph Example</div> | ||
<div class="background-sample bg-neutral-100 "> | ||
${generateTemplate({ | ||
args: overrideArgs([ | ||
{ | ||
type: 'slot', | ||
name: 'default', | ||
value: | ||
'<div class="sd-paragraph">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nibh justo ullamcorper odio tempor molestie phasellus dui vel id. Velit in sed non orci pellentesque vivamus nunc. At non tortor, sit neque tristique. Facilisis commodo integer hendrerit tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nibh justo ullamcorper odio tempor molestie phasellus dui vel id. Velit in sed non orci pellentesque vivamus nunc. At non tortor, sit neque tristique. Facilisis commodo integer hendrerit tortor.</div>' | ||
}, | ||
{ | ||
type: 'cssProperty', | ||
name: '--gradient-color-start', | ||
value: 'rgba(246, 246, 246, 0)' | ||
}, | ||
{ | ||
type: 'cssProperty', | ||
name: '--gradient-color-end', | ||
value: 'rgba(246, 246, 246, 1)' | ||
} | ||
]) | ||
})} | ||
</div> | ||
`; | ||
} | ||
}; |
108 changes: 108 additions & 0 deletions
108
packages/components/src/components/expandable/expandable.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { expect, fixture, html, waitUntil } from '@open-wc/testing'; | ||
import sinon from 'sinon'; | ||
import type SdExpandable from './expandable'; | ||
|
||
describe('<sd-expandable>', () => { | ||
it('should be accessible', async () => { | ||
const el = await fixture<SdExpandable>(html` | ||
<sd-expandable> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore | ||
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo | ||
consequat. | ||
</sd-expandable> | ||
`); | ||
|
||
await expect(el).to.be.accessible(); | ||
}); | ||
|
||
it('should be visible when the open attribute is set', async () => { | ||
const el = await fixture<SdExpandable>(html` | ||
<sd-expandable open> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore | ||
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo | ||
consequat. | ||
</sd-expandable> | ||
`); | ||
|
||
expect(el.open).to.be.true; | ||
expect(el.shadowRoot!.querySelector<HTMLElement>('details')?.getAttribute('open')).to.equal('true'); | ||
}); | ||
|
||
it('should not be visible without the open attribute', async () => { | ||
const el = await fixture<SdExpandable>(html` | ||
<sd-expandable> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore | ||
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo | ||
consequat. | ||
</sd-expandable> | ||
`); | ||
|
||
expect(el.open).to.be.false; | ||
expect(el.shadowRoot!.querySelector<HTMLElement>('details')?.getAttribute('open')).to.equal('false'); | ||
}); | ||
|
||
it('should emit sd-show and sd-after-show when opened programmatically', async () => { | ||
const el = await fixture<SdExpandable>(html` | ||
<sd-expandable> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore | ||
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo | ||
consequat. | ||
</sd-expandable> | ||
`); | ||
const showHandler = sinon.spy(); | ||
const afterShowHandler = sinon.spy(); | ||
|
||
el.addEventListener('sd-show', showHandler); | ||
el.addEventListener('sd-after-show', afterShowHandler); | ||
|
||
el.show(); // Programmatically open the expandable | ||
await el.updateComplete; | ||
|
||
await waitUntil(() => showHandler.calledOnce); | ||
await waitUntil(() => afterShowHandler.calledOnce); | ||
|
||
expect(showHandler).to.have.been.calledOnce; | ||
expect(afterShowHandler).to.have.been.calledOnce; | ||
expect(el.shadowRoot!.querySelector<HTMLElement>('details')?.getAttribute('open')).to.equal('true'); | ||
}); | ||
|
||
it('should emit sd-hide and sd-after-hide when closed programmatically', async () => { | ||
const el = await fixture<SdExpandable>(html` | ||
<sd-expandable open> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore | ||
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo | ||
consequat. | ||
</sd-expandable> | ||
`); | ||
const hideHandler = sinon.spy(); | ||
const afterHideHandler = sinon.spy(); | ||
|
||
el.addEventListener('sd-hide', hideHandler); | ||
el.addEventListener('sd-after-hide', afterHideHandler); | ||
el.hide(); // Programmatically close the expandable | ||
|
||
await waitUntil(() => hideHandler.calledOnce); | ||
await waitUntil(() => afterHideHandler.calledOnce); | ||
|
||
expect(hideHandler).to.have.been.calledOnce; | ||
expect(afterHideHandler).to.have.been.calledOnce; | ||
expect(el.shadowRoot!.querySelector<HTMLElement>('details')?.getAttribute('open')).to.equal('false'); | ||
}); | ||
|
||
it('should toggle open state when toggle button is clicked', async () => { | ||
const el = await fixture<SdExpandable>(html` | ||
<sd-expandable> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore | ||
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo | ||
consequat. | ||
</sd-expandable> | ||
`); | ||
const toggleButton = el.shadowRoot?.querySelector('button'); | ||
toggleButton?.click(); // Simulate user clicking the toggle button | ||
|
||
expect(el.open).to.be.true; | ||
|
||
toggleButton?.click(); // Click again to close | ||
expect(el.open).to.be.false; | ||
}); | ||
}); |
Oops, something went wrong.