Skip to content

Commit

Permalink
#14079 - Allow usage of all component valid selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
dexster committed Mar 13, 2021
1 parent 94a86bd commit 972cb87
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,102 @@ describe('angular source decorator', () => {
});
});

describe('with component with attribute selector', () => {
@Component({
selector: 'doc-button[foo]',
template: '<button></button>',
})
class WithAttributeComponent {}

it('should add attribute to template', async () => {
const component = WithAttributeComponent;
const props = {};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(`<doc-button foo></doc-button>`);
});
});

describe('with component with attribute and value selector', () => {
@Component({
selector: 'doc-button[foo=bar]',
template: '<button></button>',
})
class WithAttributeValueComponent {}

it('should add attribute to template', async () => {
const component = WithAttributeValueComponent;
const props = {};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(`<doc-button foo="bar"></doc-button>`);
});
});

describe('with component with attribute only selector', () => {
@Component({
selector: '[foo]',
template: '<button></button>',
})
class WithAttributeOnlyComponent {}

it('should create a div and add attribute to template', async () => {
const component = WithAttributeOnlyComponent;
const props = {};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(`<div foo></div>`);
});
});

describe('with component with class selector', () => {
@Component({
selector: 'doc-button.foo',
template: '<button></button>',
})
class WithClassComponent {}

it('should add class to template', async () => {
const component = WithClassComponent;
const props = {};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(`<doc-button class="foo"></doc-button>`);
});
});

describe('with component with class only selector', () => {
@Component({
selector: '.foo',
template: '<button></button>',
})
class WithClassComponent {}

it('should create a div and add attribute to template', async () => {
const component = WithClassComponent;
const props = {};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(`<div class="foo"></div>`);
});
});

describe('with component with multiple selectors', () => {
@Component({
selector: 'doc-button, doc-button2',
template: '<button></button>',
})
class WithMultipleSelectorsComponent {}

it('should use the first selector', async () => {
const component = WithMultipleSelectorsComponent;
const props = {};
const argTypes: ArgTypes = {};
const source = computesTemplateSourceFromComponent(component, props, argTypes);
expect(source).toEqual(`<doc-button></doc-button>`);
});
});

describe('no argTypes', () => {
it('should generate tag-only template with no props', () => {
const component = InputComponent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export const computesTemplateFromComponent = (
? ` ${initialOutputs.map((i) => `(${i})="${i}($event)"`).join(' ')}`
: '';

return `<${ngComponentMetadata.selector}${templateInputs}${templateOutputs}>${innerTemplate}</${ngComponentMetadata.selector}>`;
const template = buildTemplate(ngComponentMetadata.selector);
return `<${template.openTag}${templateInputs}${templateOutputs}>${innerTemplate}</${template.closeTag}>`;
};

const createAngularInputProperty = ({
Expand Down Expand Up @@ -127,5 +128,55 @@ export const computesTemplateSourceFromComponent = (
? ` ${initialOutputs.map((i) => `(${i})="${i}($event)"`).join(' ')}`
: '';

return `<${ngComponentMetadata.selector}${templateInputs}${templateOutputs}></${ngComponentMetadata.selector}>`;
const template = buildTemplate(ngComponentMetadata.selector);
return `<${template.openTag}${templateInputs}${templateOutputs}></${template.closeTag}>`;
};

const buildTemplate = (
selector: string
): {
openTag?: string;
closeTag?: string;
} => {
const templates = [
{
// Match element selectors with optional chained attributes or classes
re: /^([\w\d-_]+)(?:(?:\[([\w\d-_]+)(?:=(.+))?\])|\.([\w\d-_]+))?/,
openTag: (matched: string[]) => {
let template = matched[1];
if (matched[2]) {
template += ` ${matched[2]}`;
}
if (matched[3]) {
template += `="${matched[3]}"`;
}
if (matched[4]) {
template += ` class="${matched[4]}"`;
}
return template;
},
closeTag: (matched: string[]) => `${matched[1]}`,
},
{
re: /^\.(.+)/,
openTag: (matched: string[]) => `div class="${matched[1]}"`,
closeTag: (matched: string[]) => `div`,
},
{
re: /^\[([\w\d-_]+)(?:=(.+))?\]/,
openTag: (matched: string[]) => `div ${matched[1]} ${matched[2] ? `="${matched[2]}"` : ''}`,
closeTag: (matched: string[]) => `div`,
},
];

return templates.reduce((acc, template) => {
const matched = selector.match(template.re);
if (matched) {
return {
openTag: template.openTag(matched).trim(),
closeTag: template.closeTag(matched),
};
}
return acc;
}, {});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots Basics / Component / With Complex Selectors attribute selectors 1`] = `
<storybook-wrapper>
<storybook-attribute-selector
foo=""
>
<p>
Attribute selector
</p>
</storybook-attribute-selector>
</storybook-wrapper>
`;

exports[`Storyshots Basics / Component / With Complex Selectors attribute value selectors 1`] = `
<storybook-wrapper>
<storybook-attribute-value-selector
foo="bar"
>
<p>
Attribute with value
</p>
</storybook-attribute-value-selector>
</storybook-wrapper>
`;

exports[`Storyshots Basics / Component / With Complex Selectors multiple selectors 1`] = `
<storybook-wrapper>
<storybook-multiple-selector>
<p>
Multiple selector
</p>
</storybook-multiple-selector>
</storybook-wrapper>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Component } from '@angular/core';

@Component({
selector: 'storybook-attribute-selector[foo]',
template: '<p>Attribute selector</p>',
})
export class AttributeSelectorComponent {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Component } from '@angular/core';

@Component({
selector: 'storybook-attribute-value-selector[foo=bar]',
template: '<p>Attribute with value</p>',
})
export class AttributeWithValueSelectorComponent {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MultipleSelectorComponent } from './multiple-selector.component';
import { AttributeSelectorComponent } from './attribute-selector.component';
import { AttributeWithValueSelectorComponent } from './attributewithvalue-selector.component';

export default {
title: 'Basics / Component / With Complex Selectors',
};

export const MultipleSelectors = () => ({});

MultipleSelectors.storyName = 'multiple selectors';
MultipleSelectors.parameters = {
component: MultipleSelectorComponent,
};

export const AttributeSelectors = () => ({});

AttributeSelectors.storyName = 'attribute selectors';
AttributeSelectors.parameters = {
component: AttributeSelectorComponent,
};

export const AttributeValueSelectors = () => ({});

AttributeValueSelectors.storyName = 'attribute value selectors';
AttributeValueSelectors.parameters = {
component: AttributeWithValueSelectorComponent,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Component } from '@angular/core';

@Component({
selector: 'storybook-multiple-selector, storybook-multiple-selector2',
template: '<p>Multiple selector</p>',
})
export class MultipleSelectorComponent {}

0 comments on commit 972cb87

Please sign in to comment.