From de212664a643a63ed81a70e0e92228113e63c5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Tue, 15 Sep 2026 22:07:14 +0200 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9E=95=20Add=20error=20slot=20for=20Text?= =?UTF-8?q?Input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/TextInput.stories.ts | 26 +++++++++++++++++++++ src/components/TextInput.vue | 33 ++++++++++++++++---------- test/components/TextInput.test.js | 36 +++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 12 deletions(-) diff --git a/src/components/TextInput.stories.ts b/src/components/TextInput.stories.ts index 55ad3cf..be4e16b 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..462e5d9 100644 --- a/test/components/TextInput.test.js +++ b/test/components/TextInput.test.js @@ -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.help-label.invalid'); + 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.help-label.invalid'); + expect(textInputErr.exists()).toBe(true); + + const textInput = wrapper.find('input'); + expect(textInput.attributes().class).toContain('error'); + }); }); From ed21c2f09b4caf8ef8ab1146bd4880e85ed3d330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Tue, 15 Sep 2026 22:29:58 +0200 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=A8=20Fix=20tests=20and=20markup?= =?UTF-8?q?=20inconsistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/TextInput.stories.ts | 2 +- test/components/TextInput.test.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/TextInput.stories.ts b/src/components/TextInput.stories.ts index be4e16b..dbfbf46 100644 --- a/src/components/TextInput.stories.ts +++ b/src/components/TextInput.stories.ts @@ -230,7 +230,7 @@ export const Error: Story = {
`, diff --git a/test/components/TextInput.test.js b/test/components/TextInput.test.js index 462e5d9..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']); @@ -444,7 +444,7 @@ describe('TextInput', () => { }, }); - const textInputErr = wrapper.find('span.help-label.invalid'); + 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'); @@ -461,7 +461,7 @@ describe('TextInput', () => { }, }); - const textInputErr = wrapper.find('span.help-label.invalid'); + const textInputErr = wrapper.find('span.error-label'); expect(textInputErr.exists()).toBe(true); const textInput = wrapper.find('input');