Skip to content

Commit

Permalink
Remove for attr in <label> if it contains <input>
Browse files Browse the repository at this point in the history
Fixes issue described in #727
  • Loading branch information
sergeche committed Mar 13, 2024
1 parent 9fee6ee commit 88aba52
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/markup/addon/label.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { AbbreviationAttribute, AbbreviationNode } from '@emmetio/abbreviation';
import { find } from '../utils.js';

/**
* Preprocessor of `<label>` element: if it contains `<input>`, remove `for` attribute
* and `id` from input
*/
export default function label(node: AbbreviationNode) {
if (node.name === 'label') {
const input = find(node, n => (n.name === 'input' || n.name === 'textarea'));
if (input) {
// Remove empty `for` attribute
if (node.attributes) {
node.attributes = node.attributes.filter(attr => {
return !(attr.name === 'for' && isEmptyAttribute(attr));
});
}

// Remove empty `id` attribute
if (input.attributes) {
input.attributes = input.attributes.filter(attr => {
return !(attr.name === 'id' && isEmptyAttribute(attr));
});
}
}
}
}

function isEmptyAttribute(attr: AbbreviationAttribute) {
if (!attr.value) {
return true;
}

if (attr.value.length === 1) {
const token = attr.value[0];
if (token && typeof token !== 'string' && !token.name) {
// Attribute contains field
return true;
}
}

return false;
}
5 changes: 5 additions & 0 deletions src/markup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import implicitTag from './implicit-tag.js';
import lorem from './lorem/index.js';
import xsl from './addon/xsl.js';
import bem from './addon/bem.js';
import label from './addon/label.js';
import html from './format/html.js';
import haml from './format/haml.js';
import slim from './format/slim.js';
Expand Down Expand Up @@ -72,6 +73,10 @@ function transform(node: AbbreviationNode, ancestors: Container[], config: Confi
xsl(node);
}

if (config.type === 'markup') {
label(node);
}

if (config.options['bem.enabled']) {
bem(node, ancestors, config);
}
Expand Down
17 changes: 17 additions & 0 deletions src/markup/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@ export function walk<S>(node: Container, fn: WalkVisitor<S>, state?: S) {
node.children.forEach(callback);
}

/**
* Finds first child node that matches given `callback`
*/
export function find(node: Container, callback: (node: AbbreviationNode) => boolean | undefined): AbbreviationNode | undefined {
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
if (callback(child)) {
return child;
}

const result = find(child, callback);
if (result) {
return result;
}
}
}

/**
* Finds node which is the deepest for in current node or node itself.
*/
Expand Down
7 changes: 7 additions & 0 deletions test/markup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ describe('Markup abbreviations', () => {
equal(expand('xsl:with-param[select]{foo}', config), '<xsl:with-param>foo</xsl:with-param>');
});

it('<label> preprocessor', () => {
equal(expand('label>input'), '<label><input type="${1:text}" /></label>');
equal(expand('label>inp'), '<label><input type="${1:text}" name="${1}" /></label>');
equal(expand('label>span>input'), '<label><span><input type="${1:text}" /></span></label>');
equal(expand('label+inp'), '<label for=""></label><input type="${1:text}" name="${1}" id="${1}" />');
});

describe('BEM transform', () => {
const config = resolveConfig({
options: { 'bem.enabled': true }
Expand Down

0 comments on commit 88aba52

Please sign in to comment.