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: `
+
+
+
+ Your session has expired. Log in again.
+
+
+ `,
+ }),
+ parameters: {
+ docs: {
+ source: {
+ code: `
+
+
+ Your session has expired. Log in again.
+
+`,
+ },
+ },
+ },
+};
+
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');
+ });
});