Skip to content
Open
8 changes: 8 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ <h1>Form Elements</h1>
label="Connection Field"
options='[{"id":"1","label":"John Doe"},{"id":"2","label":"Jane Smith","user":true},{"id":"3","label":"Trevor Virtue","user":true},{"id":"4","label":"Jane Meldrum"}]'
></dt-connection>

<dt-multi-text
id="phone-field"
name="phone-field"
label="Phone Field"
type="phone"
value='[{"value":"+1-555-123-4567","key":"phone_1"},{"value":"19995551234","key":"phone_2"}]'
></dt-multi-text>
</dt-tile>
</body>
</html>
51 changes: 51 additions & 0 deletions src/components/form/dt-multi-text/dt-multi-text.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { when } from 'lit/directives/when.js';
import { DtText } from '../dt-text/dt-text.js';
import '../../icons/dt-icon.js';
import '../../layout/dt-phone-modal/dt-phone-modal.js';

/**
* Field to edit multiple text values with ability to add/remove values.
Expand Down Expand Up @@ -144,6 +145,22 @@ export class DtMultiText extends DtText {
.field-container:has(.btn-remove) ~ .icon-overlay {
inset-inline-end: 5.5rem;
}

.input-addon.btn-phone-open {
color: var(--primary-color, #0073aa);
display: flex;
align-items: center;
gap: 0.25rem;
white-space: nowrap;
aspect-ratio: auto;
&:disabled {
color: var(--dt-text-placeholder-color, #999);
}
&:hover:not([disabled]) {
background-color: var(--primary-color, #0073aa);
color: var(--dt-multi-text-button-hover-color, #ffffff);
}
}
`,
];
}
Expand Down Expand Up @@ -252,7 +269,26 @@ export class DtMultiText extends DtText {
}
}

_openPhoneModal(e) {
// Use 'this' to comply with class method expectations
const { phoneNumber } = e.currentTarget.dataset;
if (phoneNumber) {
// Get or create the phone modal
let modal = document.querySelector('dt-phone-modal');
if (!modal) {
modal = document.createElement('dt-phone-modal');
document.body.appendChild(modal);
}
// Optionally, store a reference to the modal on this instance if needed
this._phoneModal = modal;
modal.open(phoneNumber);
}
}

_inputFieldTemplate(item, itemCount) {
const isPhone = this.type === 'phone' || this.type === 'phone-intl';
const hasPhoneValue = isPhone && item.value && item.value.trim() !== '';

return html`
<div class="field-container">
<input
Expand All @@ -269,6 +305,21 @@ export class DtMultiText extends DtText {
novalidate
/>

${when(
hasPhoneValue,
() => html`
<button
class="input-addon btn-phone-open"
@click=${this._openPhoneModal}
data-phone-number="${item.value}"
?disabled=${this.disabled}
title="Send a message"
aria-label="Send a message to ${item.value}"
>
<dt-icon icon="mdi:phone-outgoing"></dt-icon> Open
</button>
`,
)}
${when(
itemCount > 1 || item.key || item.value,
() => html`
Expand Down
20 changes: 19 additions & 1 deletion src/components/form/dt-multi-text/dt-multi-text.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default {
placeholder: { control: 'text' },
type: {
control: 'select',
options: ['text', 'password', 'email', 'number', 'tel', 'url'],
options: ['text', 'password', 'email', 'number', 'tel', 'url', 'phone'],
defaultValue: 'text',
},
disabled: { control: 'boolean' },
Expand Down Expand Up @@ -177,6 +177,24 @@ password.args = {
type: 'password',
};

export const phone = Template.bind({});
phone.args = {
type: 'phone',
label: 'Phone Numbers',
value: [
{
verified: false,
value: '+1-555-123-4567',
key: 'phone_1',
},
{
verified: false,
value: '19995551234',
key: 'phone_2',
},
],
};

export const requiredCustomMessage = Template.bind({});
requiredCustomMessage.args = {
required: true,
Expand Down
140 changes: 140 additions & 0 deletions src/components/form/dt-multi-text/dt-multi-text.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { html } from 'lit';
import { fixture, expect, oneEvent, aTimeout, nextFrame } from '@open-wc/testing';
import { sendKeys } from '@web/test-runner-commands';
import './dt-multi-text.js';
import '../../layout/dt-phone-modal/dt-phone-modal.js';

describe('DtMultiText', () => {

Expand Down Expand Up @@ -168,4 +169,143 @@ describe('DtMultiText', () => {
expect(detail.newValue[0].value).to.equal('Value 1')
expect(detail.newValue[0].delete).to.equal(true);
});

describe('Phone type functionality', () => {
it('shows "Open" button when type is phone and field has value', async () => {
const el = await fixture(
html`<dt-multi-text
type="phone"
value="${JSON.stringify([{
key: 'cc01',
value: '+1-555-123-4567',
verified: true,
}])}"
></dt-multi-text>`
);

await nextFrame();

const phoneButton = el.shadowRoot.querySelector('.btn-phone-open');
expect(phoneButton).to.exist;
expect(phoneButton.textContent.trim()).to.include('Open');
expect(phoneButton.dataset.phoneNumber).to.equal('+1-555-123-4567');
});

it('hides "Open" button when phone field is empty', async () => {
const el = await fixture(
html`<dt-multi-text type="phone"></dt-multi-text>`
);

await nextFrame();

const phoneButton = el.shadowRoot.querySelector('.btn-phone-open');
expect(phoneButton).to.not.exist;
});

it('shows "Open" button when type is phone-intl and field has value', async () => {
const el = await fixture(
html`<dt-multi-text
type="phone-intl"
value="${JSON.stringify([{
key: 'cc01',
value: '+33123456789',
verified: true,
}])}"
></dt-multi-text>`
);

await nextFrame();

const phoneButton = el.shadowRoot.querySelector('.btn-phone-open');
expect(phoneButton).to.exist;
expect(phoneButton.dataset.phoneNumber).to.equal('+33123456789');
});

it('does not show "Open" button for non-phone types', async () => {
const el = await fixture(
html`<dt-multi-text
type="email"
value="${JSON.stringify([{
key: 'cc01',
value: 'test@example.com',
verified: true,
}])}"
></dt-multi-text>`
);

await nextFrame();

const phoneButton = el.shadowRoot.querySelector('.btn-phone-open');
expect(phoneButton).to.not.exist;
});

it('opens phone modal when "Open" button is clicked', async () => {
const el = await fixture(
html`<dt-multi-text
type="phone"
value="${JSON.stringify([{
key: 'cc01',
value: '+1-555-123-4567',
verified: true,
}])}"
></dt-multi-text>`
);

await nextFrame();

// Remove any existing phone modals
const existingModals = document.querySelectorAll('dt-phone-modal');
existingModals.forEach(modal => modal.remove());

const phoneButton = el.shadowRoot.querySelector('.btn-phone-open');
expect(phoneButton).to.exist;

phoneButton.click();
await aTimeout(50);

// Check that a phone modal was created
const phoneModal = document.querySelector('dt-phone-modal');
expect(phoneModal).to.exist;
expect(phoneModal.phoneNumber).to.equal('+1-555-123-4567');
expect(phoneModal.isOpen).to.be.true;

// Clean up
phoneModal.remove();
});

it('updates "Open" button visibility when phone value changes', async () => {
const el = await fixture(
html`<dt-multi-text type="phone"></dt-multi-text>`
);

await nextFrame();

// Initially no button should be visible
let phoneButton = el.shadowRoot.querySelector('.btn-phone-open');
expect(phoneButton).to.not.exist;

// Add a phone number value
const input = el.shadowRoot.querySelector('input');
input.focus();
input.value = '+1-555-123-4567';
input.dispatchEvent(new Event('change'));

await nextFrame();

// Now button should be visible
phoneButton = el.shadowRoot.querySelector('.btn-phone-open');
expect(phoneButton).to.exist;
expect(phoneButton.dataset.phoneNumber).to.equal('+1-555-123-4567');

// Clear the value
input.value = '';
input.dispatchEvent(new Event('change'));

await nextFrame();

// Button should be hidden again
phoneButton = el.shadowRoot.querySelector('.btn-phone-open');
expect(phoneButton).to.not.exist;
});
});
});
Loading
Loading