Skip to content

Commit

Permalink
FIO 8151: allow for reference attr name (#5567)
Browse files Browse the repository at this point in the history
* make the reference attribute name configurable

* updates for formatting and types

* fallback to ref

* add jsdoc annotation

* fix linting error
  • Loading branch information
brendanbond authored Apr 23, 2024
1 parent d0b5e7e commit 12bcdc9
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 29 deletions.
2 changes: 2 additions & 0 deletions src/Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export default class Element {
*
* @param {string} event - The event you wish to register the handler for.
* @param {function} cb - The callback handler to handle this event.
* @param {boolean} [internal] - This is an internal event handler.
* @param {boolean} [once] - This event should only fire once.
*/
on(event, cb, internal, once = false) {
if (!this.events) {
Expand Down
16 changes: 8 additions & 8 deletions src/WebformBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export default class WebformBuilder extends Component {
return element;
}
// Attach container and component to element for later reference.
const containerElement = element.querySelector(`[ref="${component.component.key}-container"]`) || element;
const containerElement = element.querySelector(`[${this._referenceAttributeName}="${component.component.key}-container"]`) || element;
containerElement.formioContainer = container;
containerElement.formioComponent = component;

Expand Down Expand Up @@ -1175,7 +1175,7 @@ export default class WebformBuilder extends Component {

this.preview.form.components.forEach(component => this.replaceDoubleQuotes(component, fieldsToRemoveDoubleQuotes));

const previewElement = this.componentEdit.querySelector('[ref="preview"]');
const previewElement = this.componentEdit.querySelector(`[${this._referenceAttributeName}="preview"]`);
if (previewElement) {
this.setContent(previewElement, this.preview.render(), null, sanitizeConfig);
this.preview.attach(previewElement);
Expand Down Expand Up @@ -1384,7 +1384,7 @@ export default class WebformBuilder extends Component {
}

attachEditComponentControls(component, parent, isNew, original, ComponentClass) {
const cancelButtons = this.componentEdit.querySelectorAll('[ref="cancelButton"]');
const cancelButtons = this.componentEdit.querySelectorAll(`[${this._referenceAttributeName}="cancelButton"]`);
cancelButtons.forEach((cancelButton) => {
this.editForm.addEventListener(cancelButton, 'click', (event) => {
event.preventDefault();
Expand All @@ -1395,7 +1395,7 @@ export default class WebformBuilder extends Component {
});
});

const removeButtons = this.componentEdit.querySelectorAll('[ref="removeButton"]');
const removeButtons = this.componentEdit.querySelectorAll(`[${this._referenceAttributeName}="removeButton"]`);
removeButtons.forEach((removeButton) => {
this.editForm.addEventListener(removeButton, 'click', (event) => {
event.preventDefault();
Expand All @@ -1408,7 +1408,7 @@ export default class WebformBuilder extends Component {
});
});

const saveButtons = this.componentEdit.querySelectorAll('[ref="saveButton"]');
const saveButtons = this.componentEdit.querySelectorAll(`[${this._referenceAttributeName}="saveButton"]`);
saveButtons.forEach((saveButton) => {
this.editForm.addEventListener(saveButton, 'click', (event) => {
event.preventDefault();
Expand All @@ -1425,7 +1425,7 @@ export default class WebformBuilder extends Component {
});
});

const previewButtons = this.componentEdit.querySelectorAll('[ref="previewButton"]');
const previewButtons = this.componentEdit.querySelectorAll(`[${this._referenceAttributeName}="previewButton"]`);
previewButtons.forEach((previewButton) => {
this.editForm.addEventListener(previewButton, 'click', (event) => {
event.preventDefault();
Expand All @@ -1438,7 +1438,7 @@ export default class WebformBuilder extends Component {
showPreview: this.showPreview,
helplinks: this.helplinks,
}));
this.editForm.attach(this.componentEdit.querySelector('[ref="editForm"]'));
this.editForm.attach(this.componentEdit.querySelector(`[${this._referenceAttributeName}="editForm"]`));
this.attachEditComponentControls(component, parent, isNew, original, ComponentClass);
});
});
Expand Down Expand Up @@ -1556,7 +1556,7 @@ export default class WebformBuilder extends Component {
this.dialog = this.createModal(this.componentEdit, _.get(this.options, 'dialogAttr', {}));

// This is the attach step.
this.editForm.attach(this.componentEdit.querySelector('[ref="editForm"]'));
this.editForm.attach(this.componentEdit.querySelector(`[${this._referenceAttributeName}="editForm"]`));

this.hook('editFormWrapper');

Expand Down
34 changes: 26 additions & 8 deletions src/components/_classes/component/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,11 @@ export default class Component extends Element {
this._visible = this._parentVisible && this.conditionallyVisible(null, data);
this._parentDisabled = false;

/**
* The reference attribute name for this component
*/
this._referenceAttributeName = 'ref';

/**
* Used to trigger a new change in this component.
* @type {function} - Call to trigger a change in this component.
Expand Down Expand Up @@ -910,25 +915,26 @@ export default class Component extends Element {
const templatesByName = Templates.defaultTemplates[name];

if (!templatesByName) {
return `Unknown template: ${name}`;
return { template: `Unknown template: ${name}` };
}

const templateByMode = this.checkTemplateMode(templatesByName, modes);
if (templateByMode) {
return templateByMode;
return { template: templateByMode };
}

return templatesByName.form;
return { template: templatesByName.form };
}

checkTemplate(templates, names, modes) {
for (const name of names) {
const templatesByName = templates[name];

if (templatesByName) {
const { referenceAttributeName } = templatesByName;
const templateByMode = this.checkTemplateMode(templatesByName, modes);
if (templateByMode) {
return templateByMode;
return { template: templateByMode, referenceAttributeName };
}
}
}
Expand Down Expand Up @@ -995,9 +1001,13 @@ export default class Component extends Element {
];

// Allow template alters.
const { referenceAttributeName, template } = this.getTemplate(names, mode);
if (referenceAttributeName) {
this._referenceAttributeName = referenceAttributeName;
}
return this.hook(
`render${name.charAt(0).toUpperCase() + name.substring(1, name.length)}`,
this.interpolate(this.getTemplate(names, mode), data),
this.interpolate(template, data),
data,
mode
);
Expand Down Expand Up @@ -1138,12 +1148,20 @@ export default class Component extends Element {
return currentTimezone();
}

loadRefs(element, refs) {
/**
*
* @param {HTMLElement} element - The containing DOM element to query for the ref value.
* @param {object} refs - The references to load.
* @param {string} [referenceAttributeName] - The attribute name to use for the reference.
*/
loadRefs(element, refs, referenceAttributeName) {
for (const ref in refs) {
const refType = refs[ref];
const isString = typeof refType === 'string';

const selector = isString && refType.includes('scope') ? `:scope > [ref="${ref}"]` : `[ref="${ref}"]`;
const selector = isString && refType.includes('scope')
? `:scope > [${referenceAttributeName || this._referenceAttributeName || 'ref'}="${ref}"]`
: `[${referenceAttributeName || this._referenceAttributeName || 'ref'}="${ref}"]`;

if (isString && refType.startsWith('single')) {
this.refs[ref] = element.querySelector(selector);
Expand Down Expand Up @@ -1230,7 +1248,7 @@ export default class Component extends Element {
}

createComponentModal(element, modalShouldBeOpened, currentValue) {
return new ComponentModal(this, element, modalShouldBeOpened, currentValue);
return new ComponentModal(this, element, modalShouldBeOpened, currentValue, this._referenceAttributeName);
}

attach(element) {
Expand Down
9 changes: 5 additions & 4 deletions src/components/_classes/componentModal/ComponentModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export default class ComponentModal {
});
}

constructor(component, element, isOpened, currentValue) {
constructor(component, element, isOpened, currentValue, referenceAttributeName = 'ref') {
this._referenceAttributeName = referenceAttributeName;
this.isOpened = isOpened;
this.component = component;
this.element = element;
Expand Down Expand Up @@ -165,10 +166,10 @@ export default class ComponentModal {
showDialog() {
this.dialogElement = this.component.ce('div');
const dialogContent = `
<h3 ref="dialogHeader">${this.component.t('Do you want to clear changes?')}</h3>
<h3 ${this._referenceAttributeName}="dialogHeader">${this.component.t('Do you want to clear changes?')}</h3>
<div style="display:flex; justify-content: flex-end;">
<button ref="dialogCancelButton" class="btn btn-secondary">${this.component.t('Cancel')}</button>
<button ref="dialogYesButton" class="btn btn-danger">${this.component.t('Yes, delete it')}</button>
<button ${this._referenceAttributeName}="dialogCancelButton" class="btn btn-secondary">${this.component.t('Cancel')}</button>
<button ${this._referenceAttributeName}="dialogYesButton" class="btn btn-danger">${this.component.t('Yes, delete it')}</button>
</div>
`;

Expand Down
2 changes: 1 addition & 1 deletion src/components/_classes/input/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export default class Input extends Multivalue {
}).trim();
if (this.component.prefix !== calendarIcon) {
// converting string to HTML markup to render correctly DateTime component in portal.form.io
return convertStringToHTMLElement(calendarIcon, '[ref="icon"]');
return convertStringToHTMLElement(calendarIcon, `[${this._referenceAttributeName}="icon"]`);
}
}
return this.component.suffix;
Expand Down
2 changes: 1 addition & 1 deletion src/components/_classes/multivalue/Multivalue.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default class Multivalue extends Field {
// If single value field.
if (!this.useWrapper()) {
return super.render(
`<div ref="element">
`<div ${this._referenceAttributeName}="element">
${this.renderElement(
this.component.type !== 'hidden' ? this.dataValue : ''
)}
Expand Down
2 changes: 1 addition & 1 deletion src/components/datagrid/DataGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ export default class DataGridComponent extends NestedArrayComponent {
super.loadRefs(element, refs);

if (refs['messageContainer'] === 'single') {
const container = _.last(element.querySelectorAll('[ref=messageContainer]'));
const container = _.last(element.querySelectorAll(`[${this._referenceAttributeName}=messageContainer]`));
this.refs['messageContainer'] = container || this.refs['messageContainer'];
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/editgrid/EditGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ export default class EditGridComponent extends NestedArrayComponent {

get defaultDialogTemplate() {
return `
<h3 ref="dialogHeader">${this.t('Do you want to clear data?')}</h3>
<h3 ${this._referenceAttributeName}="dialogHeader">${this.t('Do you want to clear data?')}</h3>
<div style="display:flex; justify-content: flex-end;">
<button ref="dialogCancelButton" class="btn btn-secondary" aria-label="${this.t('Cancel')}">${this.t('Cancel')}</button>
<button ref="dialogYesButton" class="btn btn-danger" aria-label="${this.t('Yes, delete it')}">${this.t('Yes, delete it')}</button>
<button ${this._referenceAttributeName}="dialogCancelButton" class="btn btn-secondary" aria-label="${this.t('Cancel')}">${this.t('Cancel')}</button>
<button ${this._referenceAttributeName}="dialogYesButton" class="btn btn-danger" aria-label="${this.t('Yes, delete it')}">${this.t('Yes, delete it')}</button>
</div>
`;
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/form/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ export default class FormComponent extends Component {
if (!this.builderMode && this.component.modalEdit) {
const modalShouldBeOpened = this.componentModal ? this.componentModal.isOpened : false;
const currentValue = modalShouldBeOpened ? this.componentModal.currentValue : this.dataValue;
this.componentModal = new ComponentModal(this, element, modalShouldBeOpened, currentValue);
this.componentModal = new ComponentModal(this, element, modalShouldBeOpened, currentValue, this._referenceAttributeName);
this.setOpenModalElement();
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/signature/Signature.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export default class SignatureComponent extends Input {
getModalPreviewTemplate() {
return this.renderTemplate('modalPreview', {
previewText: this.dataValue ?
`<img src=${this.dataValue} ref='openModal' style="width: 100%;height: 100%;" />` :
`<img src=${this.dataValue} ${this._referenceAttributeName}='openModal' style="width: 100%;height: 100%;" />` :
this.t('Click to Sign')
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/textarea/TextArea.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default class TextAreaComponent extends TextFieldComponent {
info.content = value;
if ((this.options.readOnly || this.disabled) && !this.isHtmlRenderMode()) {
const elementStyle = this.info.attr.style || '';
const children = `<div ref="input" class="formio-editor-read-only-content" ${elementStyle ? `style='${elementStyle}'` : ''}></div>`;
const children = `<div ${this._referenceAttributeName}="input" class="formio-editor-read-only-content" ${elementStyle ? `style='${elementStyle}'` : ''}></div>`;

return this.renderTemplate('well', {
children,
Expand Down

0 comments on commit 12bcdc9

Please sign in to comment.