Skip to content

Commit 23aff70

Browse files
authored
Merge branch 'main' into changeset-release/main
2 parents fae3d77 + b7ea0a0 commit 23aff70

File tree

6 files changed

+346
-25
lines changed

6 files changed

+346
-25
lines changed

.changeset/twenty-vans-joke.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@justeattakeaway/pie-select": minor
3+
"pie-storybook": minor
4+
---
5+
6+
[Added] - shell component and its main props
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,111 @@
1-
import { html } from 'lit';
1+
import { html, nothing } from 'lit';
22
import { type Meta } from '@storybook/web-components';
33

44
import '@justeattakeaway/pie-select';
5-
import { type SelectProps } from '@justeattakeaway/pie-select';
5+
import {
6+
defaultProps,
7+
sizes,
8+
statusTypes,
9+
type SelectProps as SelectBaseProps,
10+
} from '@justeattakeaway/pie-select';
11+
import '@justeattakeaway/pie-icons-webc/dist/IconPlaceholder.js';
12+
import { ifDefined } from 'lit/directives/if-defined.js';
613

7-
import { createStory } from '../utilities';
14+
import { createStory, type TemplateFunction } from '../utilities';
815

16+
type SelectProps = SelectBaseProps & { showLeadingIcon: boolean };
917
type SelectStoryMeta = Meta<SelectProps>;
1018

11-
const defaultArgs: SelectProps = {};
19+
const defaultArgs: SelectProps = {
20+
...defaultProps,
21+
name: 'testName',
22+
showLeadingIcon: false,
23+
};
1224

1325
const selectStoryMeta: SelectStoryMeta = {
1426
title: 'Select',
1527
component: 'pie-select',
16-
argTypes: {},
28+
argTypes: {
29+
name: {
30+
description: 'The name of the select (used as a key/value pair with `value`). This is required in order to work properly with forms.',
31+
control: 'text',
32+
defaultValue: {
33+
summary: defaultArgs.name,
34+
},
35+
},
36+
disabled: {
37+
description: 'If true, disables the select field.',
38+
control: 'boolean',
39+
defaultValue: {
40+
summary: defaultProps.disabled,
41+
},
42+
},
43+
size: {
44+
description: 'The size of the select field. Can be `small`, `medium` or `large`. Defaults to `medium`.',
45+
control: 'select',
46+
options: sizes,
47+
defaultValue: {
48+
summary: defaultProps.size,
49+
},
50+
},
51+
assistiveText: {
52+
description: 'An optional assistive text to display below the select element. Must be provided when the status is success or error.',
53+
control: 'text',
54+
defaultValue: {
55+
summary: '',
56+
},
57+
},
58+
status: {
59+
description: 'The status of the select component / assistive text. Can be default or error.',
60+
control: 'select',
61+
options: statusTypes,
62+
defaultValue: {
63+
summary: defaultProps.status,
64+
},
65+
},
66+
showLeadingIcon: {
67+
description: '<b>**Not a component prop</b><br><br>Use the `leadingIcon` slot to pass a PIE icon component.',
68+
control: 'boolean',
69+
defaultValue: {
70+
summary: defaultArgs.showLeadingIcon,
71+
},
72+
},
73+
},
1774
args: defaultArgs,
1875
parameters: {
1976
design: {
2077
type: 'figma',
21-
url: '',
78+
url: 'https://www.figma.com/design/pPSC73rPin4csb8DiK1CRr/branch/7dcPx40PggJCudTmgHwlk6/%E2%9C%A8-%5BCore%5D-Web-Components-%5BPIE-3%5D?node-id=1573-114525&p=f&m=dev',
2279
},
2380
},
2481
};
2582

2683
export default selectStoryMeta;
2784

28-
// TODO: remove the eslint-disable rule when props are added
29-
// eslint-disable-next-line no-empty-pattern
30-
const Template = ({}: SelectProps) => html`
31-
<pie-select></pie-select>
32-
`;
85+
const Template = ({
86+
disabled,
87+
size,
88+
assistiveText,
89+
status,
90+
name,
91+
showLeadingIcon,
92+
}: SelectProps) => html`
93+
<pie-select
94+
id="${ifDefined(name)}"
95+
name="${ifDefined(name)}"
96+
?disabled="${disabled}"
97+
size="${ifDefined(size)}"
98+
assistiveText="${ifDefined(assistiveText)}"
99+
status=${ifDefined(status)}>
100+
${showLeadingIcon ? html`<icon-placeholder slot="leadingIcon"></icon-placeholder>` : nothing}
101+
</pie-select>
102+
`;
103+
104+
const WithLabelTemplate: TemplateFunction<SelectProps> = (props: SelectProps) => html`
105+
<p>Please note, the label is a separate component. See <pie-link href="/?path=/story/form-label">pie-form-label</pie-link>.</p>
106+
<pie-form-label for="${ifDefined(props.name)}">Label</pie-form-label>
107+
${Template(props)}
108+
`;
33109

34110
export const Default = createStory<SelectProps>(Template, defaultArgs)();
111+
export const Labelled = createStory<SelectProps>(WithLabelTemplate, defaultArgs)();
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
11
import type React from 'react';
2-
/**
3-
* TODO: Verify if ReactBaseType can be set as a more specific React interface
4-
* Use the React IntrinsicElements interface to find how to map standard HTML elements to existing React Interfaces
5-
* Example: an HTML button maps to `React.ButtonHTMLAttributes<HTMLButtonElement>`
6-
* https://github.com/DefinitelyTyped/DefinitelyTyped/blob/0bb210867d16170c4a08d9ce5d132817651a0f80/types/react/index.d.ts#L2829
7-
*/
8-
export type ReactBaseType = React.HTMLAttributes<HTMLElement>
2+
3+
export type ReactBaseType = React.HTMLAttributes<HTMLSelectElement>
+40-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,40 @@
1-
// TODO - please remove the eslint disable comment below when you add props to this interface
2-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
3-
export interface SelectProps {}
1+
import { type ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';
2+
3+
export const sizes = ['small', 'medium', 'large'] as const;
4+
5+
export const statusTypes = ['default', 'error'] as const;
6+
7+
export interface SelectProps {
8+
/**
9+
* The size of the select component. Can be `small`, `medium` or `large`. Defaults to `medium`.
10+
*/
11+
size?: typeof sizes[number];
12+
13+
/**
14+
* Same as the HTML disabled attribute - indicates whether the select is disabled.
15+
*/
16+
disabled?: boolean;
17+
18+
/**
19+
* An optional assistive text to display below the select element. Must be provided when the status is success or error.
20+
*/
21+
assistiveText?: string;
22+
23+
/**
24+
* The status of the select component / assistive text. Can be default or error.
25+
*/
26+
status?: typeof statusTypes[number];
27+
28+
/**
29+
* The name of the select (used as a key/value pair with `value`). This is required in order to work properly with forms.
30+
*/
31+
name?: string;
32+
}
33+
34+
type DefaultProps = ComponentDefaultProps<SelectProps, keyof Omit<SelectProps, 'name' | 'assistiveText' >>;
35+
36+
export const defaultProps: DefaultProps = {
37+
size: 'medium',
38+
status: 'default',
39+
disabled: false,
40+
};

packages/components/pie-select/src/index.ts

+110-4
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,126 @@
1-
import { LitElement, html, unsafeCSS } from 'lit';
2-
import { RtlMixin, defineCustomElement } from '@justeattakeaway/pie-webc-core';
1+
import {
2+
LitElement,
3+
html,
4+
nothing,
5+
unsafeCSS,
6+
} from 'lit';
7+
import {
8+
RtlMixin,
9+
defineCustomElement,
10+
validPropertyValues,
11+
} from '@justeattakeaway/pie-webc-core';
12+
import {
13+
property,
14+
query,
15+
queryAssignedElements,
16+
state,
17+
} from 'lit/decorators.js';
18+
import { ifDefined } from 'lit/directives/if-defined.js';
19+
import { classMap, type ClassInfo } from 'lit/directives/class-map.js';
20+
import '@justeattakeaway/pie-icons-webc/dist/IconChevronDown.js';
321

422
import styles from './select.scss?inline';
5-
import { type SelectProps } from './defs';
23+
import {
24+
defaultProps,
25+
sizes,
26+
statusTypes,
27+
type SelectProps,
28+
} from './defs';
629

730
// Valid values available to consumers
831
export * from './defs';
932

1033
const componentSelector = 'pie-select';
34+
const assistiveTextIdValue = 'assistive-text';
1135

1236
/**
1337
* @tagname pie-select
1438
*/
1539
export class PieSelect extends RtlMixin(LitElement) implements SelectProps {
40+
static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
41+
42+
@property({ type: String })
43+
@validPropertyValues(componentSelector, sizes, defaultProps.size)
44+
public size = defaultProps.size;
45+
46+
@property({ type: Boolean })
47+
public disabled = defaultProps.disabled;
48+
49+
@property({ type: String })
50+
@validPropertyValues(componentSelector, statusTypes, defaultProps.status)
51+
public status = defaultProps.status;
52+
53+
@property({ type: String })
54+
public assistiveText: SelectProps['assistiveText'];
55+
56+
@property({ type: String })
57+
public name: SelectProps['name'];
58+
59+
@query('select')
60+
public focusTarget!: HTMLElement;
61+
62+
@queryAssignedElements({ slot: 'leadingIcon', flatten: true })
63+
private _leadingIconSlot!: Array<HTMLElement>;
64+
65+
@state()
66+
private _hasLeadingIcon = false;
67+
68+
private _handleLeadingIconSlotchange () {
69+
this._hasLeadingIcon = Boolean(this._leadingIconSlot.length);
70+
}
71+
72+
private renderAssistiveText () {
73+
if (!this.assistiveText) {
74+
return nothing;
75+
}
76+
77+
return html`
78+
<pie-assistive-text
79+
id="${assistiveTextIdValue}"
80+
variant=${ifDefined(this.status)}
81+
data-test-id="pie-textarea-assistive-text">
82+
${this.assistiveText}
83+
</pie-assistive-text>
84+
`;
85+
}
86+
1687
render () {
17-
return html`<h1 data-test-id="pie-select">Hello world!</h1>`;
88+
const {
89+
assistiveText,
90+
disabled,
91+
status,
92+
size,
93+
name,
94+
_hasLeadingIcon,
95+
} = this;
96+
97+
const classes : ClassInfo = {
98+
'c-select': true,
99+
[`c-select--${size}`]: true,
100+
[`c-select--${status}`]: true,
101+
'c-select--withLeadingIcon': _hasLeadingIcon,
102+
'is-disabled': disabled,
103+
};
104+
105+
return html`
106+
<div
107+
class="${classMap(classes)}"
108+
data-test-id="pie-select-shell">
109+
<slot name="leadingIcon" @slotchange=${this._handleLeadingIconSlotchange}></slot>
110+
<select
111+
name=${ifDefined(name)}
112+
?disabled=${disabled}
113+
aria-describedby=${ifDefined(assistiveText ? assistiveTextIdValue : undefined)}
114+
aria-invalid=${status === 'error' ? 'true' : 'false'}
115+
aria-errormessage="${ifDefined(status === 'error' ? assistiveTextIdValue : undefined)}">
116+
<option value="dog">Dog</option>
117+
<option value="cat">Cat</option>
118+
<option value="hamster">Hamster</option>
119+
</select>
120+
<icon-chevron-down size='s' class='c-select-trailingIcon'></icon-chevron-down>
121+
</div>
122+
${this.renderAssistiveText()}
123+
`;
18124
}
19125

20126
// Renders a `CSSResult` generated from SCSS by Vite

0 commit comments

Comments
 (0)