Skip to content

Commit 39338c9

Browse files
authored
IBX-10346: Rewrite existing components to twig components (#7)
1 parent f8a7fa0 commit 39338c9

38 files changed

+771
-438
lines changed

prettier.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export default {
88
options: {
99
parser: 'twig',
1010
plugins: ['@zackad/prettier-plugin-twig'],
11+
twigOutputEndblockName: true,
1112
},
1213
},
1314
],

src/bundle/DependencyInjection/IbexaDesignSystemTwigExtension.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public function load(array $configs, ContainerBuilder $container): void
4040
public function prepend(ContainerBuilder $container): void
4141
{
4242
$this->prependDefaultConfiguration($container);
43+
$this->prependTwigComponentConfiguration($container);
4344
$this->prependJMSTranslation($container);
4445
}
4546

@@ -55,6 +56,13 @@ private function prependDefaultConfiguration(ContainerBuilder $container): void
5556
}
5657
}
5758

59+
private function prependTwigComponentConfiguration(ContainerBuilder $container): void
60+
{
61+
$twigComponentConfigFile = realpath(__DIR__ . '/../Resources/config/ibexa_twig_component.yaml');
62+
$container->prependExtensionConfig('twig_component', Yaml::parseFile($twigComponentConfigFile));
63+
$container->addResource(new FileResource($twigComponentConfigFile));
64+
}
65+
5866
private function prependJMSTranslation(ContainerBuilder $container): void
5967
{
6068
$container->prependExtensionConfig('jms_translation', [
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
anonymous_template_directory: 'components/'
2+
defaults:
3+
# Namespace & directory for components
4+
App\Twig\Components\: 'components/'
5+
6+
Ibexa\DesignSystemTwig\Twig\Components\:
7+
template_directory: '@ibexadesign/design_system/components/'
8+
name_prefix: ibexa

src/bundle/Resources/public/ts/components/accordion.ts

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
1+
import Expander, { ExpanderType } from './expander';
12
import Base from '../shared/Base';
2-
import { ExpanderType } from './expander';
33

4-
import { HTMLElementIbexaInstance } from '../shared/types';
4+
import { HTMLElementIDSInstance } from '../shared/types';
55

6-
import { getInstance } from '../helpers/object.instances';
76
import { reflow } from '../helpers/dom';
87

9-
class Accordion extends Base {
10-
private _togglerElement: HTMLElementIbexaInstance<ExpanderType> | null;
8+
export default class Accordion extends Base {
9+
private _togglerElement: HTMLElementIDSInstance<ExpanderType> | null;
1110
private _togglerInstance: ExpanderType;
1211
private _contentElement: HTMLElement | null;
1312

1413
constructor(container: HTMLElement) {
1514
super(container);
1615

17-
this._togglerElement = container.querySelector<HTMLElementIbexaInstance<ExpanderType>>('.ibexa-expander');
16+
this._togglerElement = container.querySelector<HTMLElementIDSInstance<ExpanderType>>('.ids-expander');
1817

1918
if (!this._togglerElement) {
2019
throw new Error('No toggler element found for this container!');
2120
}
2221

23-
this._togglerInstance = getInstance<ExpanderType>(this._togglerElement);
24-
this._contentElement = container.querySelector<HTMLElement>('.ibexa-accordion__content');
22+
this._togglerInstance = new Expander(this._togglerElement);
23+
this._contentElement = container.querySelector<HTMLElement>('.ids-accordion__content');
2524

2625
this.onToggleClick = this.onToggleClick.bind(this);
2726
}
2827

2928
isExpanded(): boolean {
30-
return this.container.classList.contains('ibexa-accordion--is-expanded');
29+
return this.container.classList.contains('ids-accordion--is-expanded');
3130
}
3231

3332
toggleIsExpanded(isExpanded: boolean) {
34-
const prevIsExpanded = this.container.classList.contains('ibexa-accordion--is-expanded');
33+
const prevIsExpanded = this.container.classList.contains('ids-accordion--is-expanded');
3534

3635
if (prevIsExpanded !== isExpanded) {
3736
this._togglerInstance.toggleIsExpanded(isExpanded);
@@ -47,12 +46,12 @@ class Accordion extends Base {
4746

4847
reflow(this._contentElement);
4948

50-
this.container.classList.toggle('ibexa-accordion--is-expanded', isExpanded);
51-
this.container.classList.toggle('ibexa-accordion--is-animating', true);
49+
this.container.classList.toggle('ids-accordion--is-expanded', isExpanded);
50+
this.container.classList.toggle('ids-accordion--is-animating', true);
5251
this._contentElement.addEventListener(
5352
'transitionend',
5453
() => {
55-
this.container.classList.toggle('ibexa-accordion--is-animating', false);
54+
this.container.classList.toggle('ids-accordion--is-animating', false);
5655

5756
if (this._contentElement) {
5857
this._contentElement.style.removeProperty('height');
@@ -71,6 +70,8 @@ class Accordion extends Base {
7170
}
7271

7372
initToggler() {
73+
this._togglerInstance.init();
74+
7475
this._togglerInstance.expandHandler = this.onToggleClick.bind(this);
7576
}
7677

@@ -81,10 +82,4 @@ class Accordion extends Base {
8182
}
8283
}
8384

84-
const accordionContainers = document.querySelectorAll<HTMLElement>('.ibexa-accordion:not([custom-init])');
85-
86-
accordionContainers.forEach((accordionContainer: HTMLElement) => {
87-
const accordionInstance = new Accordion(accordionContainer);
88-
89-
accordionInstance.init();
90-
});
85+
export type AccordionType = InstanceType<typeof Accordion>;

src/bundle/Resources/public/ts/components/expander.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,19 @@ export default class Expander extends Base {
77
private _hasLabel: boolean;
88
private _collapseLabel: string | undefined;
99
private _expandLabel: string | undefined;
10+
private _labelContainer: HTMLElement;
1011

1112
constructor(container: HTMLElement) {
1213
super(container);
1314

14-
this._hasLabel = this.container.classList.contains('ibexa-expander--has-label');
15+
const labelContainer = this.container.querySelector<HTMLElement>('.ids-expander__label');
16+
17+
if (!labelContainer) {
18+
throw new Error('No label container found for this expander!');
19+
}
20+
21+
this._labelContainer = labelContainer;
22+
this._hasLabel = this.container.classList.contains('ids-expander--has-label');
1523

1624
if (this._hasLabel) {
1725
this._collapseLabel = container.dataset.collapseLabel;
@@ -24,14 +32,14 @@ export default class Expander extends Base {
2432
}
2533

2634
isExpanded(): boolean {
27-
return this.container.classList.contains('ibexa-expander--is-expanded');
35+
return this.container.classList.contains('ids-expander--is-expanded');
2836
}
2937

3038
toggleIsExpanded(isExpanded: boolean) {
31-
this.container.classList.toggle('ibexa-expander--is-expanded', isExpanded);
39+
this.container.classList.toggle('ids-expander--is-expanded', isExpanded);
3240

33-
if (this._hasLabel) {
34-
this.container.innerHTML = (isExpanded ? this._collapseLabel : this._expandLabel) ?? '';
41+
if (this._hasLabel && this._collapseLabel && this._expandLabel) {
42+
this._labelContainer.innerHTML = isExpanded ? this._collapseLabel : this._expandLabel;
3543
}
3644
}
3745

@@ -49,11 +57,3 @@ export default class Expander extends Base {
4957
}
5058

5159
export type ExpanderType = InstanceType<typeof Expander>;
52-
53-
const expanderContainers = document.querySelectorAll<HTMLElement>('.ibexa-expander:not([custom-init])');
54-
55-
expanderContainers.forEach((expanderContainer: HTMLElement) => {
56-
const expanderInstance = new Expander(expanderContainer);
57-
58-
expanderInstance.init();
59-
});

src/bundle/Resources/public/ts/components/inputs/InputText.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,17 @@ export default class InpuText extends Base {
88
constructor(container: HTMLDivElement) {
99
super(container);
1010

11-
this._actionsElement = container.querySelector('.ids-input-text__actions') as HTMLDivElement;
12-
this._inputElement = container.querySelector('.ids-input-text__source .ids-input') as HTMLInputElement;
13-
this._clearBtnElement = this._actionsElement.querySelector('.ids-clear-btn') as HTMLButtonElement;
11+
const actionsElement = container.querySelector<HTMLDivElement>('.ids-input-text__actions');
12+
const inputElement = container.querySelector<HTMLInputElement>('.ids-input-text__source .ids-input');
13+
const clearBtnElement = actionsElement?.querySelector<HTMLButtonElement>('.ids-clear-btn');
14+
15+
if (!actionsElement || !inputElement || !clearBtnElement) {
16+
throw new Error('InputText: Required elements are missing in the container.');
17+
}
18+
19+
this._actionsElement = actionsElement;
20+
this._inputElement = inputElement;
21+
this._clearBtnElement = clearBtnElement;
1422
}
1523

1624
private _updateInputPadding() {
Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
import { HTMLElementIbexaInstance } from '../shared/types';
1+
import { HTMLElementIDSInstance } from '../shared/types';
22

3-
const setInstance = <InstanceType>(domElement: HTMLElementIbexaInstance<InstanceType>, instance: InstanceType): void => {
4-
if (domElement.ibexaInstance) {
3+
const setInstance = <InstanceType>(domElement: HTMLElementIDSInstance<InstanceType>, instance: InstanceType): void => {
4+
if (domElement.idsInstance) {
55
throw new Error('Instance for this DOM element already exists!');
66
}
77

8-
domElement.ibexaInstance = instance; // eslint-disable-line no-param-reassign
8+
domElement.idsInstance = instance; // eslint-disable-line no-param-reassign
99
};
10-
const hasInstance = <InstanceType>(domElement: HTMLElementIbexaInstance<InstanceType>): boolean => {
11-
return !!domElement.ibexaInstance;
10+
const hasInstance = <InstanceType>(domElement: HTMLElementIDSInstance<InstanceType>): boolean => {
11+
return !!domElement.idsInstance;
1212
};
13-
const getInstance = <InstanceType>(domElement: HTMLElementIbexaInstance<InstanceType>): InstanceType => {
14-
if (domElement.ibexaInstance) {
15-
return domElement.ibexaInstance;
13+
const getInstance = <InstanceType>(domElement: HTMLElementIDSInstance<InstanceType>): InstanceType => {
14+
if (domElement.idsInstance) {
15+
return domElement.idsInstance;
1616
}
1717

1818
throw new Error('Instance for this DOM element doesn\'t exists!');
1919
};
20-
const clearInstance = <InstanceType>(domElement: HTMLElementIbexaInstance<InstanceType>): void => {
21-
delete domElement.ibexaInstance; // eslint-disable-line no-param-reassign
20+
const clearInstance = <InstanceType>(domElement: HTMLElementIDSInstance<InstanceType>): void => {
21+
delete domElement.idsInstance; // eslint-disable-line no-param-reassign
2222
};
2323

2424
export { setInstance, getInstance, clearInstance, hasInstance };

src/bundle/Resources/public/ts/init_components.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
import InputText from './components/ui/InputText';
1+
import Accordion from './components/accordion';
2+
import InputText from './components/inputs/InputText';
23

3-
const inputTextContainers = document.querySelectorAll<HTMLElement>('.ids-input-text:not([custom-init])');
4+
const accordionContainers = document.querySelectorAll<HTMLDivElement>('.ids-accordion:not([custom-init])');
45

5-
inputTextContainers.forEach((inputTextContainer: HTMLElement) => {
6+
accordionContainers.forEach((accordionContainer: HTMLDivElement) => {
7+
const accordionInstance = new Accordion(accordionContainer);
8+
9+
accordionInstance.init();
10+
});
11+
12+
const inputTextContainers = document.querySelectorAll<HTMLDivElement>('.ids-input-text:not([custom-init])');
13+
14+
inputTextContainers.forEach((inputTextContainer: HTMLDivElement) => {
615
const inputTextInstance = new InputText(inputTextContainer);
716

817
inputTextInstance.init();

src/bundle/Resources/public/ts/shared/Base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ export default abstract class Base {
1010
}
1111

1212
init() {
13-
this.container.dispatchEvent(new CustomEvent('ibexa-ds:component:initialized', { detail: { component: this } }));
13+
this.container.dispatchEvent(new CustomEvent('ids:component:initialized', { detail: { component: this } }));
1414
}
1515
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export interface HTMLElementIbexaInstance<InstanceType> extends HTMLElement {
2-
ibexaInstance?: InstanceType;
1+
export interface HTMLElementIDSInstance<InstanceType> extends HTMLElement {
2+
idsInstance?: InstanceType;
33
}

0 commit comments

Comments
 (0)