diff --git a/src/components/TextInput.stories.ts b/src/components/TextInput.stories.ts index 55ad3cf..dbfbf46 100644 --- a/src/components/TextInput.stories.ts +++ b/src/components/TextInput.stories.ts @@ -223,6 +223,32 @@ export const Help: Story = { }, }; +export const Error: Story = { + render: () => ({ + components: { TextInput }, + template: ` +
+ + + + `, + }), + parameters: { + docs: { + source: { + code: ` + + +`, + }, + }, + }, +}; + export const Time: Story = { render: () => ({ components: { TextInput }, diff --git a/src/components/TextInput.vue b/src/components/TextInput.vue index ea2ea97..ecad463 100644 --- a/src/components/TextInput.vue +++ b/src/components/TextInput.vue @@ -131,7 +131,7 @@ const togglePasswordVisibility = () => { class="tbpro-input-element" :class="{ dirty: isDirty, - error: error !== null, + error: error !== null || $slots.error, }" :type="inputType" :id="name" @@ -164,13 +164,13 @@ const togglePasswordVisibility = () => { {{ outerSuffix }} - + {{ validationMessage }} - + - {{ error }} + {{ error }} {{ help }} @@ -211,15 +211,24 @@ const togglePasswordVisibility = () => { width: 100%; font-size: 0.6875rem; line-height: 0.9375rem; +} - &.invalid { - gap: 0.25rem; - border-radius: 0.25rem; - padding: 0.25rem; - font-size: 0.75rem; - background-color: var(--colour-danger-soft); - color: var(--colour-danger-default); - } +.error-label { + display: flex; + align-items: center; + color: var(--colour-ti-muted); + box-sizing: border-box; + + width: 100%; + font-size: 0.6875rem; + line-height: 0.9375rem; + + gap: 0.25rem; + border-radius: 0.25rem; + padding: 0.25rem; + font-size: 0.75rem; + background-color: var(--colour-danger-soft); + color: var(--colour-danger-default); } .required { diff --git a/test/components/TextInput.test.js b/test/components/TextInput.test.js index 8d6e2f1..3b24dac 100644 --- a/test/components/TextInput.test.js +++ b/test/components/TextInput.test.js @@ -248,7 +248,7 @@ describe('TextInput', () => { // verify error text is there if was provided if (ourProps['error']) { - const textInputErr = wrapper.find('span.help-label.invalid'); + const textInputErr = wrapper.find('span.error-label'); expect(textInputErr.exists()).toBe(true); expect(textInputErr.isVisible()).toBe(true); expect(textInputErr.text()).toBe(ourProps['error']); @@ -431,4 +431,40 @@ describe('TextInput', () => { expect(textInputHelp.find('button').exists()).toBe(true); expect(textInputHelp.text()).toBe('Use suggestion'); }); + + it('renders the error slot content instead of the error prop when provided', () => { + wrapper = mount(TextInput, { + props: { + name: 'text-input-error-slot-test', + label: 'Username', + error: 'This should not be rendered.', + }, + slots: { + error: 'Log in again', + }, + }); + + const textInputErr = wrapper.find('span.error-label'); + expect(textInputErr.exists()).toBe(true); + expect(textInputErr.find('a').exists()).toBe(true); + expect(textInputErr.text()).toBe('Log in again'); + }); + + it('triggers the invalid styling from the error slot alone, with no error prop set', () => { + wrapper = mount(TextInput, { + props: { + name: 'text-input-error-slot-only-test', + label: 'Username', + }, + slots: { + error: 'Log in again', + }, + }); + + const textInputErr = wrapper.find('span.error-label'); + expect(textInputErr.exists()).toBe(true); + + const textInput = wrapper.find('input'); + expect(textInput.attributes().class).toContain('error'); + }); });