Skip to content
Merged
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
78 changes: 78 additions & 0 deletions libs/core/src/components.d.ts

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions libs/core/src/components/pds-input/docs/pds-input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ Removes the ability to interact with the text field. If a value is present, that
<pds-input component-id="pds-input-readonly-example" label="Name" type="email" readonly="true" value="Frank Dux"></pds-input>
</DocCanvas>

### Highlight

Applies accent styling to the input field with a purple background, border, and text color to draw attention to important fields.

<DocCanvas client:only
mdxSource={{
react: '<PdsInput componentId="pds-input-highlight-example" label="Label" type="text" highlight="true" value="This input is highlighted"></PdsInput>',
webComponent: '<pds-input component-id="pds-input-highlight-example" label="Label" type="text" highlight="true" value="This input is highlighted"></pds-input>'
}}>
<pds-input component-id="pds-input-highlight-example" label="Label" type="text" highlight="true" value="This input is highlighted"></pds-input>
</DocCanvas>

### Full Width

When `full-width` is set to `true`, the input component will occupy the full width of its parent container instead of using its natural inline width.
Expand Down
16 changes: 16 additions & 0 deletions libs/core/src/components/pds-input/pds-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,22 @@
}
}

:host([highlight]:not([disabled]):not([aria-disabled="true"]):not([invalid]):not([aria-readonly="true"])) {
--pds-input-background: var(--pine-color-accent-disabled);
--pds-input-border-color: var(--pine-color-accent);
--pds-input-border-color-hover: var(--pine-color-accent-hover);
--pds-input-text-color: var(--pine-color-text-accent);

&::part(prefix),
&::part(suffix) {
color: var(--pine-color-text-accent);
}

.pds-input__field:focus-visible {
outline-color: var(--pine-color-focus-ring);
}
}

.pds-input {
display: flex;
flex-direction: column;
Expand Down
5 changes: 5 additions & 0 deletions libs/core/src/components/pds-input/pds-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ export class PdsInput {
*/
@Prop() fullWidth?: boolean;

/**
* Applies highlight styling to the input field.
*/
@Prop({ reflect: true }) highlight?: boolean;

/**
* Determines if the input has focus.
*/
Expand Down
1 change: 1 addition & 0 deletions libs/core/src/components/pds-input/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
| `fullWidth` | `full-width` | Determines whether or not the input field takes full width of its container. | `boolean` | `undefined` |
| `helperMessage` | `helper-message` | Displays a message or hint below the input field. | `string` | `undefined` |
| `hideLabel` | `hide-label` | Visually hides the label text for instances where only the input should be displayed. Label remains accessible to assistive technology such as screen readers. Note: When true, the action slot is also hidden to maintain a minimal UI. | `boolean` | `undefined` |
| `highlight` | `highlight` | Applies highlight styling to the input field. | `boolean` | `undefined` |
| `invalid` | `invalid` | Determines whether or not the input field is invalid or throws an error. | `boolean` | `undefined` |
| `label` | `label` | Text to be displayed as the input label. | `string` | `undefined` |
| `max` | `max` | Specifies the maximum value for the input field. | `string` | `undefined` |
Expand Down
37 changes: 37 additions & 0 deletions libs/core/src/components/pds-input/stories/pds-input.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default {
fullWidth: false,
helperMessage: null,
hideLabel: false,
highlight: false,
invalid: false,
max: null,
maxlength: null,
Expand Down Expand Up @@ -48,6 +49,7 @@ const BaseTemplate = (args) => html`<pds-input
?full-width=${args.fullWidth}
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="${args.label}"
name="${args.name}"
Expand Down Expand Up @@ -149,6 +151,31 @@ FullWidth.args = {
placeholder: 'This input takes full width of its container',
};

export const Highlight = (args) => html`<pds-input
autocomplete="${args.autocomplete}"
component-id="pds-input-highlight-example"
debounce="${args.debounce}"
?disabled=${args.disabled}
error-message="${args.errorMessage}"
?full-width=${args.fullWidth}
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="${args.label || 'Label'}"
name="${args.name}"
placeholder="${args.placeholder}"
?readonly=${args.readonly}
?required=${args.required}
type="${args.type || 'text'}"
value="${args.value}">
<pds-icon slot="prefix" name="mail" size="small"></pds-icon>
</pds-input>`;
Highlight.args = {
highlight: true,
value: 'This input is highlighted',
};

export const withPrefixIcon = (args) => html`<pds-input
autocomplete="${args.autocomplete}"
component-id="pds-input-prefix-icon"
Expand All @@ -157,6 +184,7 @@ export const withPrefixIcon = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Email"
name="${args.name}"
Expand All @@ -176,6 +204,7 @@ export const withSuffixButton = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Search"
name="${args.name}"
Expand All @@ -197,6 +226,7 @@ export const withPrependSelect = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Amount"
name="${args.name}"
Expand All @@ -220,6 +250,7 @@ export const withPrependButton = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Search Query"
name="${args.name}"
Expand All @@ -241,6 +272,7 @@ export const withAppendSelect = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Phone"
name="${args.name}"
Expand All @@ -264,6 +296,7 @@ export const withAppendButton = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Search Query"
name="${args.name}"
Expand All @@ -285,6 +318,7 @@ export const withPrefixAndAppend = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Amount"
name="${args.name}"
Expand All @@ -309,6 +343,7 @@ export const withPrependAndSuffix = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Amount"
name="${args.name}"
Expand All @@ -335,6 +370,7 @@ export const withActionLink = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="${args.helperMessage}"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Password"
name="${args.name}"
Expand All @@ -356,6 +392,7 @@ export const withActionButton = (args) => html`<pds-input
error-message="${args.errorMessage}"
helper-message="Choose a unique username"
?hide-label=${args.hideLabel}
?highlight=${args.highlight}
?invalid=${args.invalid}
label="Username"
name="${args.name}"
Expand Down
36 changes: 36 additions & 0 deletions libs/core/src/components/pds-input/test/pds-input.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,40 @@ describe('pds-input', () => {
expect(component).toHaveAttribute('has-action');
expect(await component.getAttribute('has-action')).toBe('true');
});

it('applies highlight styling when highlight prop is set', async () => {
const page = await newE2EPage();
await page.setContent('<pds-input highlight value="test"></pds-input>');

const component = await page.find('pds-input');
expect(component).toHaveClass('hydrated');
expect(component).toHaveAttribute('highlight');

// Verify highlight attribute is reflected
const hasHighlight = await component.getAttribute('highlight');
expect(hasHighlight).not.toBeNull();
});

it('toggles highlight attribute when property changes', async () => {
const page = await newE2EPage();
await page.setContent('<pds-input></pds-input>');
const component = await page.find('pds-input');

// Initially no highlight
expect(component).not.toHaveAttribute('highlight');

// Set highlight property
component.setProperty('highlight', true);
await page.waitForChanges();

// Should have highlight attribute
expect(component).toHaveAttribute('highlight');

// Unset highlight property
component.setProperty('highlight', false);
await page.waitForChanges();

// Should not have highlight attribute
expect(component).not.toHaveAttribute('highlight');
});
});
57 changes: 56 additions & 1 deletion libs/core/src/components/pds-input/test/pds-input.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ describe('pds-input', () => {
it('renders label wrapper without visually-hidden class when hideLabel is false', async () => {
const { root } = await newSpecPage({
components: [PdsInput],
html: `<pds-input component-id="field-1" label="Name" hide-label="false"></pds-input>`,
html: `<pds-input component-id="field-1" label="Name"></pds-input>`,
});

const labelWrapper = root?.shadowRoot?.querySelector('.pds-input__label-wrapper');
Expand All @@ -849,4 +849,59 @@ describe('pds-input', () => {
expect(input?.hasAttribute('aria-label')).toBeFalsy();
});
});

describe('highlight', () => {
it('renders with highlight attribute when highlight prop is true', async () => {
const { root } = await newSpecPage({
components: [PdsInput],
html: `<pds-input component-id="field-1" highlight="true" value="Frank Dux"></pds-input>`
});

expect(root?.hasAttribute('highlight')).toBe(true);
});

it('reflects highlight property to attribute', async () => {
const page = await newSpecPage({
components: [PdsInput],
html: `<pds-input component-id="field-1"></pds-input>`
});

const component = page.rootInstance;
const root = page.root;

// Initially no highlight
expect(root?.hasAttribute('highlight')).toBe(false);

// Set highlight property
component.highlight = true;
await page.waitForChanges();

// Attribute should be reflected
expect(root?.hasAttribute('highlight')).toBe(true);

// Unset highlight property
component.highlight = false;
await page.waitForChanges();

// Attribute should be removed
expect(root?.hasAttribute('highlight')).toBe(false);
});

it('semantic states take precedence over highlight', async () => {
const page = await newSpecPage({
components: [PdsInput],
html: `<pds-input component-id="field-1" highlight="true" disabled="true" value="Frank Dux"></pds-input>`
});

const root = page.root;

// Both attributes should be present
expect(root?.hasAttribute('highlight')).toBe(true);
expect(root?.hasAttribute('disabled')).toBe(true);

// Disabled state should apply (CSS selector excludes disabled)
const wrapper = root?.shadowRoot?.querySelector('.pds-input__field-wrapper');
expect(wrapper?.classList.contains('is-disabled')).toBe(true);
});
});
});
Loading