mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ACS-4985] Resolved e2e test cases
[ACS-4985] Revert test fix for e2e execution. Updated search.config.ts for e2e test cases Testing global install of webdriver-manager for e2e webdriver fix [ACS-4985] Resolved e2e test failures [ACS-4985] Increased value for drag emulation to account for increased width of the side filters panel in demo-shell search results window. [ACS-4985] Resolved unit test failures [ACS-4985] Resolved lint issues [ACS-4985] Moved new components to v6.2.0 in versionIndex.md [ACS-4985] Added documentation to versionIndex.md and README.md [ACS-4985] Moved inLast input field back to input type number. [ACS-4985] Resolved issue where typing a special character after adding some numbers in the 'In the last' input field would clear out the field [ACS-4985] Updated test cases after query generation changes [ACS-4985] Added missing null check when generating query [ACS-4985] Added *ngFor back to the search-date-range-advanced-tabbed.component.html [ACS-4985] Updated query generation logic. Now both 'In the last' and 'Between' options use the start date to end date query format [ACS-4985] Removed ANY case from switch (will be handled by default case) [ACS-4985] Split declarations into multiple lines [ACS-4985] Fixed code smell in regex [ACS-4985] Updated dates in documentation [ACS-4985] Updated documentation [ACS-4985] Added link for AngularMaterial bug for CSS workaround [ACS-4985] Added test cases for end date validation. Fixed minor issue when setting invalid date error on between date form fields [ACS-4985] Added validation when user manually enters the start and end dates [ACS-4985] Added borders to inputs [ACS-4985] Updated test cases for SearchDateRangeAdvancedTabbedComponent [ACS-4985] Transferred business logic from SearchDateRangeAdvancedComponent and SearchFilterTabbedComponent to SearchDateRangeAdvancedTabbedComponent. Updated test cases accordingly [ACS-4985] Resolved PR review comments [ACS-4985] Removed unused code from base-query-builder.service.ts [ACS-4985] Resolved linting and unit test issues [ACS-4985] Resolved minor issues where switching between tabs could sometime cause the tab content to not show up [ACS-4985] Resolved minor issues with display label creation [ACS-4985] Updated component to use MatDateFnsAdapter. BetweenStartDate and BetweenEndDate are now formatted when selected from the calender [ACS-4985] Resolved issue where clear button would not clear the values properly [ACS-4985] Added @angular/material-date-fns-adapter package [ACS-4985] Added image for updated documentation [ACS-4985] Added validation to SearchDateRangeAdvancedComponent [ACS-4985] Updated documentation for components [ACS-4985] Removed disableUpdateOnSubmit flag from search widgets [ACS-4985] Updated the documentation for the components [ACS-4985] Added test cases for SearchDateRangeAdvancedTabbedComponent. Moved pending logic from template to typescript [ACS-4985] Added clear and apply button to SearchDateRangeAdvancedTabbedComponent. Moved logic from template to typescript file [ACS-4985] Updated test cases for SearchFilterTabbedComponent. Added safety checks to component [ACS-4985] Added field validation to test case [ACS-4985] Updated SearchDateRangeAdvancedTabbed component to no longer use getters and setters [ACS-4985] Updated test cases for SearchDateRangeAdvancedComponent. Minor updates to the component template and logic. Component no longer uses getters and setters in template [ACS-4985] Updated SearchDateRangeAdvancedTabbed component to use variables instead of getters and setters [ACS-4985] Updated app.cconfig for demo-shell to use new date-range-advanced configuration ACS-4985 Fixed issue with nx build, some clean ups, using changes in configuration [ACS-4985] Updated test cases for changed date format [ACS-4985] Updated date formats for SearchDateRangeAdvancedComponent [ACS-4985] Removed fdescribe test cases for SearchDateRangeAdvancedComponent. [ACS-4985] Fixed test cases for SearchDateRangeAdvancedComponent. [ACS-4985] Fixed erroneous imports [ACS-4985] Added license headers and re-ordered imports [ACS-4985] Updated test cases for SearchDateRangeAdvancedComponent from moment.js to date-fns [ACS-4985] Migrated SearchDateRangeAdvancedComponent from moment.js to date-fns Added import for BaseQueryBuilderService in public-api.ts. Fixes #8647 [ACS-4985] Updated imports in test cases [ACS-4985] Added exports for SearchDateRangeAdvanced and SearchFilterTabbed components to public-api.ts. Updated imports in both components [ACS-4985] Resolved minor issue where the reset method would still trigger multiple api calls when used with the TabbedComponent [ACS-4985] Added test cases for SearchDateRangeAdvancedComponent. Minor update to test cases for SearchFilterTabbedComponent [ACS-4985] Updated Labels for 'In last' date range option [ACS-4985] Updated SearchModule declarations. Fixed minor typo in SearchFilterTabbedComponent [ACS-4985] Added test cases for SearchFilterTabbedComponent. Added test case placeholders for SearchDateRangeAdvancedComponent [ACS-4985] Added data-automation-id to search-date-range-advanced.component.html [ACS-4985] Added test cases for SearchFilterTabbedComponent [ACS-4985] Removed vertical mode from SearchFilterTabbedComponent [ACS-4985] Updated UI for search filters. Minor fixes [ACS-4985] Added documentation for SearchFilterTabbedComponent and SearchDateRangeAdvancedComponent [ACS-4985] Added compatibility of all search filters/facets with SearchFilterTabbedComponent [ACS-4985] Using widget-composite component now correctly updates the search query on submit. Added optional property to disable update on submit button click for widget-composite. [ACS-4985] Added SearchFilterTabbedComponent and SearchDateRangeAdvancedComponent. Added config for using the new components
This commit is contained in:
parent
c8b4083f32
commit
640a736530
@ -322,6 +322,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "createdModifiedDateRange",
|
||||||
|
"name": "Date",
|
||||||
|
"enabled": true,
|
||||||
|
"component": {
|
||||||
|
"selector": "date-range-advanced",
|
||||||
|
"settings": {
|
||||||
|
"dateFormat": "dd-MMM-yy",
|
||||||
|
"maxDate": "today",
|
||||||
|
"field": "cm:created, cm:modified",
|
||||||
|
"displayedLabelsByField": {
|
||||||
|
"cm:created": "Created Date",
|
||||||
|
"cm:modified": "Modified Date"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "queryType",
|
"id": "queryType",
|
||||||
"name": "Type",
|
"name": "Type",
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|
||||||
.app-search-settings {
|
.app-search-settings {
|
||||||
width: 260px;
|
|
||||||
border: 1px solid #eee;
|
border: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,12 +289,15 @@ for more information about installing and using the source code.
|
|||||||
| [Search Chip Input Component](content-services/components/search-chip-input.component.md) | Displays input for providing phrases display as "chips". | [Source](../lib/content-services/src/lib/search/components/search-chip-input/search-chip-input.component.ts) |
|
| [Search Chip Input Component](content-services/components/search-chip-input.component.md) | Displays input for providing phrases display as "chips". | [Source](../lib/content-services/src/lib/search/components/search-chip-input/search-chip-input.component.ts) |
|
||||||
| [Search Chip Autocomplete Input component](content-services/components/search-chip-autocomplete-input.component.md) | Displays an input with autocomplete options. | [Source](../lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.ts) |
|
| [Search Chip Autocomplete Input component](content-services/components/search-chip-autocomplete-input.component.md) | Displays an input with autocomplete options. | [Source](../lib/content-services/src/lib/search/components/search-chip-autocomplete-input/search-chip-autocomplete-input.component.ts) |
|
||||||
| [Search Chip List Component](content-services/components/search-chip-list.component.md) | Displays search criteria as a set of "chips". | [Source](../lib/content-services/src/lib/search/components/search-chip-list/search-chip-list.component.ts) |
|
| [Search Chip List Component](content-services/components/search-chip-list.component.md) | Displays search criteria as a set of "chips". | [Source](../lib/content-services/src/lib/search/components/search-chip-list/search-chip-list.component.ts) |
|
||||||
|
| [Search Date Range Advanced Component](content-services/components/search-date-range-advanced.component.md) | Displays a UI to configure different kinds of search criteria around date. Options are 'Anyytime', 'In the last' and 'Between' | [Source](../lib/content-services/src/lib/search/components/search-date-range-advanced-tabbed/search-date-range-advanced/search-date-range-advanced.component.ts) |
|
||||||
| [Search control component](content-services/components/search-control.component.md) | Displays a input text that shows find-as-you-type suggestions. | [Source](../lib/content-services/src/lib/search/components/search-control.component.ts) |
|
| [Search control component](content-services/components/search-control.component.md) | Displays a input text that shows find-as-you-type suggestions. | [Source](../lib/content-services/src/lib/search/components/search-control.component.ts) |
|
||||||
| [Search date range component](content-services/components/search-date-range.component.md) | Implements a search widget for the Search Filter component. | [Source](../lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts) |
|
| [Search date range component](content-services/components/search-date-range.component.md) | Implements a search widget for the Search Filter component. | [Source](../lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts) |
|
||||||
|
| [Search date range advanced tabbed component](content-services/components/search-date-range-advanced-tabbed.component.md) | Implements a tabbed advanced search widget for the Search Filter component. | [Source](../lib/content-services/src/lib/search/components/search-date-range-advanced-tabbed/search-date-range-advanced-tabbed.component.ts) |
|
||||||
| [Search datetime range component](content-services/components/search-datetime-range.component.md) | Implements a search widget for the Search Filter component. | [Source](../lib/content-services/src/lib/search/components/search-datetime-range/search-datetime-range.component.ts) |
|
| [Search datetime range component](content-services/components/search-datetime-range.component.md) | Implements a search widget for the Search Filter component. | [Source](../lib/content-services/src/lib/search/components/search-datetime-range/search-datetime-range.component.ts) |
|
||||||
| [Search Filter Autocomplete Chips component](content-services/components/search-filter-autocomplete-chips.component.md) | Implements a search widget for the Search Filter component. | [Source](../lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts) |
|
| [Search Filter Autocomplete Chips component](content-services/components/search-filter-autocomplete-chips.component.md) | Implements a search widget for the Search Filter component. | [Source](../lib/content-services/src/lib/search/components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component.ts) |
|
||||||
| [Search Filter Chips component](content-services/components/search-filter-chips.component.md) | Represents a chip based container component for custom search and faceted search settings. | [Source](../lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.ts) |
|
| [Search Filter Chips component](content-services/components/search-filter-chips.component.md) | Represents a chip based container component for custom search and faceted search settings. | [Source](../lib/content-services/src/lib/search/components/search-filter-chips/search-filter-chips.component.ts) |
|
||||||
| [Search Filter component](content-services/components/search-filter.component.md) | Represents a main container component for custom search and faceted search settings. | [Source](../lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts) |
|
| [Search Filter component](content-services/components/search-filter.component.md) | Represents a main container component for custom search and faceted search settings. | [Source](../lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts) |
|
||||||
|
| [Search Filter Tabbed component](content-services/components/search-filter-tabbed.component.md) | Represents a container component for creating tabbed layout. | [Source](../lib/content-services/src/lib/search/components/search-filter/search-filter.component.ts) |
|
||||||
| [Search Form component](content-services/components/search-form.component.md) | Search Form screenshot | [Source](../lib/content-services/src/lib/search/components/search-form/search-form.component.ts) |
|
| [Search Form component](content-services/components/search-form.component.md) | Search Form screenshot | [Source](../lib/content-services/src/lib/search/components/search-form/search-form.component.ts) |
|
||||||
| [Search Logical Filter component](content-services/components/search-logical-filter.component.md) | Displays 3 chip inputs each representing different logical condition for search query. | [Source](../lib/content-services/src/lib/search/components/search-logical-filter/search-logical-filter.component.ts) |
|
| [Search Logical Filter component](content-services/components/search-logical-filter.component.md) | Displays 3 chip inputs each representing different logical condition for search query. | [Source](../lib/content-services/src/lib/search/components/search-logical-filter/search-logical-filter.component.ts) |
|
||||||
| [Search Properties component](content-services/components/search-properties.component.md) | Allows to search by file size and type.| [Source](../lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts) |
|
| [Search Properties component](content-services/components/search-properties.component.md) | Allows to search by file size and type.| [Source](../lib/content-services/src/lib/search/components/search-properties/search-properties.component.ts) |
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
---
|
||||||
|
Title: Search date range advanced tabbed component
|
||||||
|
Added: v6.2.0
|
||||||
|
Status: Active
|
||||||
|
Last reviewed: 2023-07-10
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Search date range advanced tabbed component](../../../lib/content-services/src/lib/search/components/search-date-range-advanced-tabbed/search-date-range-advanced-tabbed.component.ts "Defined in search-date-range-advanced-tabbed.component.ts")
|
||||||
|
|
||||||
|
Represents a tabbed advanced date range [search widget](../../../lib/content-services/src/lib/search/models/search-widget.interface.ts) for
|
||||||
|
the [Search Filter component](search-filter.component.md).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"search": {
|
||||||
|
"categories": [
|
||||||
|
{
|
||||||
|
"id": "createdModifiedDateRange",
|
||||||
|
"name": "Date",
|
||||||
|
"enabled": true,
|
||||||
|
"component": {
|
||||||
|
"selector": "date-range-advanced",
|
||||||
|
"settings": {
|
||||||
|
"dateFormat": "dd-MMM-yy",
|
||||||
|
"maxDate": "today",
|
||||||
|
"field": "cm:created, cm:modified",
|
||||||
|
"displayedLabelsByField": {
|
||||||
|
"cm:created": "Created Date",
|
||||||
|
"cm:modified": "Modified Date"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|------------------------|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| field | string | Fields to apply the query to. Multiple, comma separated fields can be passed, to create multiple tabs per field. Required value |
|
||||||
|
| dateFormat | string | Date format. Dates used by the datepicker are Javascript Date objects, using [date-fns](https://date-fns.org/v2.30.0/docs/format) for formatting, so you can use any date format supported by the library. Default is 'dd-MMM-yy (sample date - 07-Jun-23) |
|
||||||
|
| maxDate | string | A fixed date (in format mentioned above, default format: dd-MMM-yy) or the string `"today"` that will set the maximum searchable date. Default is today. |
|
||||||
|
| displayedLabelsByField | { [key: string]: string } | A javascript object containing the different display labels to be used for each tab name, identified by the field for a particular tab. |
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
This component creates a tabbed layout where each tab consists of the [SearchDateRangeAdvanced](./search-date-range-advanced-tabbed.component.md) component, which allows user to create a query containing multiple date related queries in one go.
|
||||||
|
|
||||||
|
See the [Search filter component](search-filter.component.md) for full details of how to use widgets in a search query.
|
||||||
|
|
||||||
|
### Custom date format
|
||||||
|
|
||||||
|
You can set the date range picker to work with any date format your app requires. You can use
|
||||||
|
any date format supported by the [date-fns](https://date-fns.org/v2.30.0/docs/format) library
|
||||||
|
in the `dateFormat` and in the `maxDate` setting:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"search": {
|
||||||
|
"categories": [
|
||||||
|
{
|
||||||
|
"id": "createdModifiedDateRange",
|
||||||
|
"name": "Date",
|
||||||
|
"enabled": true,
|
||||||
|
"component": {
|
||||||
|
"selector": "date-range-advanced",
|
||||||
|
"settings": {
|
||||||
|
"dateFormat": "dd-MMM-yy",
|
||||||
|
"maxDate": "02-May-23",
|
||||||
|
"field": "cm:created, cm:modified",
|
||||||
|
"displayedLabelsByField": {
|
||||||
|
"cm:created": "Created Date",
|
||||||
|
"cm:modified": "Modified Date"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The [SearchDateRangeAdvanced](./search-date-range-advanced-tabbed.component.md) component allows 3 different kinds of date related operations to be performed.
|
||||||
|
Based on what information is provided to that component, this component will create different kinds of queries -
|
||||||
|
|
||||||
|
- Anytime - No date filters are applied on the `field`. This option is selected by default
|
||||||
|
- In the last - Allows to user to apply a filter to only show results from the last 'n' unit of time.
|
||||||
|
- Between - Allows the user to select a range of dates to filter the search results.
|
||||||
|
|
||||||
|
The queries generated by this filter when using the 'In the last' or 'Between' options is of the form -
|
||||||
|
|
||||||
|
`<field>:[<from_date> TO <to_date>]`
|
||||||
|
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Search Configuration Guide](../../user-guide/search-configuration-guide.md)
|
||||||
|
- [Search Query Builder service](../services/search-query-builder.service.md)
|
||||||
|
- [Search Widget Interface](../interfaces/search-widget.interface.md)
|
||||||
|
- [Search check list component](search-check-list.component.md)
|
||||||
|
- [Search date range component](search-date-range.component.md)
|
||||||
|
- [Search number range component](search-number-range.component.md)
|
||||||
|
- [Search radio component](search-radio.component.md)
|
||||||
|
- [Search slider component](search-slider.component.md)
|
||||||
|
- [Search text component](search-text.component.md)
|
||||||
|
- [Search filter tabbed component](search-filter-tabbed.component.md)
|
@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
Title: Search date range advanced component
|
||||||
|
Added: v6.2.0
|
||||||
|
Status: Active
|
||||||
|
Last reviewed: 2023-07-10
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Search date range advanced component](../../../lib/content-services/src/lib/search/components/search-date-range-advanced-tabbed/search-date-range-advanced/search-date-range-advanced.component.ts "Defined in search-date-range-advanced.component.ts")
|
||||||
|
|
||||||
|
Represents an advanced date range component for
|
||||||
|
the [SearchAdvancedDateRangeTabbedComponent](search-date-range-advanced-tabbed.component.md).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
```html
|
||||||
|
|
||||||
|
<adf-search-date-range-advanced></adf-search-date-range-advanced>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class Members
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|--------------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| field | string | Field to apply the query to. Required value |
|
||||||
|
| maxDate | string | A fixed date (default format: dd-MMM-yy) or the string `"today"` that will set the maximum searchable date. Default is today. |
|
||||||
|
| dateFormat | string | Date format. Dates used by the datepicker are Javascript Date objects, using [date-fns](https://date-fns.org/v2.30.0/docs/format) for formatting, so you can use any date format supported by the library. Default is 'dd-MMM-yy (sample date - 07-Jun-23) |
|
||||||
|
| initialValue | SearchDateRangeAdvanced | Initial value for the component |
|
||||||
|
|
||||||
|
### Events
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
|---------------------|------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| changed | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<Partial<SearchDateRangeAdvanced>>` | Emitted whenever a change is made in the component values. Emits the changes being made in the component. |
|
||||||
|
| valid | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<boolean>` | Emitted whenever a change is made in the component values. Emits a flag indicating whether the current state of the component is valid or not. |
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
This component lets the user choose a variety of options to perform date related operations.
|
||||||
|
|
||||||
|
- Anytime - No date related data will be returned. This option is selected by default
|
||||||
|
- In the last - Allows user to perform date related operations over a period of time. The user can select the length of the period from current time,
|
||||||
|
as well as its unit. Currently, 3 units are supported - Days, Weeks, and Months.
|
||||||
|
- Between - Allows the user to select a range of dates to perform operations on.
|
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
Title: Search filter tabbed component
|
||||||
|
Added: v6.2.0
|
||||||
|
Status: Active
|
||||||
|
Last reviewed: 2023-07-10
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Search filter tabbed component](../../../lib/content-services/src/lib/search/components/search-filter-tabbed/search-filter-tabbed.component.ts "Defined in search-filter-tabbed.component.ts")
|
||||||
|
|
||||||
|
Represents a container for [Search Filters](search-filter.component.md) to provide a tabbed user interface for the filters.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
```html
|
||||||
|
|
||||||
|
<adf-search-filter-tabbed>
|
||||||
|
<ng-container *ngFor="let field of fields">
|
||||||
|
<my-search-filter *adf-search-filter-tab="MyTabLabel"></my-search-filter>
|
||||||
|
</ng-container>
|
||||||
|
</adf-search-filter-tabbed>
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to generate a tabbed widget for multiple search filters, you can pass in the search filter widget component as a content child of the adf-search-filter-tabbed component as shown above.
|
||||||
|
|
||||||
|
Additionally, you also have to make sure that the search filter being passed as a content child of the adf-search-filter-tabbed component, also has the adf-search-filter-tabbed directive applied on it,
|
||||||
|
with the name input property being assigned the value of whatever name should be displayed for that particular tab
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Search Configuration Guide](../../user-guide/search-configuration-guide.md)
|
||||||
|
- [Search Query Builder service](../services/search-query-builder.service.md)
|
||||||
|
- [Search Widget Interface](../interfaces/search-widget.interface.md)
|
||||||
|
- [Search check list component](search-check-list.component.md)
|
||||||
|
- [Search date range component](search-date-range.component.md)
|
||||||
|
- [Search date range advanced component](search-date-range-advanced.component.md)
|
||||||
|
- [Search number range component](search-number-range.component.md)
|
||||||
|
- [Search radio component](search-radio.component.md)
|
||||||
|
- [Search slider component](search-slider.component.md)
|
||||||
|
- [Search text component](search-text.component.md)
|
@ -37,7 +37,9 @@ to build and execute the query.
|
|||||||
- [Search Widget Interface](../interfaces/search-widget.interface.md)
|
- [Search Widget Interface](../interfaces/search-widget.interface.md)
|
||||||
- [Search check list component](search-check-list.component.md)
|
- [Search check list component](search-check-list.component.md)
|
||||||
- [Search date range component](search-date-range.component.md)
|
- [Search date range component](search-date-range.component.md)
|
||||||
|
- [Search date range advanced component](search-date-range-advanced.component.md)
|
||||||
- [Search number range component](search-number-range.component.md)
|
- [Search number range component](search-number-range.component.md)
|
||||||
- [Search radio component](search-radio.component.md)
|
- [Search radio component](search-radio.component.md)
|
||||||
- [Search slider component](search-slider.component.md)
|
- [Search slider component](search-slider.component.md)
|
||||||
- [Search text component](search-text.component.md)
|
- [Search text component](search-text.component.md)
|
||||||
|
- [Search filter tabbed component](search-filter-tabbed.component.md)
|
||||||
|
BIN
docs/docassets/images/search-date-range-advanced-tabbed.png
Normal file
BIN
docs/docassets/images/search-date-range-advanced-tabbed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
docs/docassets/images/search-date-range-advanced.png
Normal file
BIN
docs/docassets/images/search-date-range-advanced.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
BIN
docs/docassets/images/search-filter-tabbed.png
Normal file
BIN
docs/docassets/images/search-filter-tabbed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
@ -48,6 +48,9 @@ backend services have been tested with each released version of ADF.
|
|||||||
<!--v620 start-->
|
<!--v620 start-->
|
||||||
|
|
||||||
- [Search Properties component](content-services/components/search-properties.component.md)
|
- [Search Properties component](content-services/components/search-properties.component.md)
|
||||||
|
- [Search Date Range Advanced Component](content-services/components/search-date-range-advanced.component.md)
|
||||||
|
- [Search Date Range Advanced Tabbed Component](content-services/components/search-date-range-advanced-tabbed.component.md)
|
||||||
|
- [Search Filter Tabbed Component](content-services/components/search-filter-tabbed.component.md)
|
||||||
|
|
||||||
<!--v620 end-->
|
<!--v620 end-->
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ describe('Search Radio Component', () => {
|
|||||||
|
|
||||||
await searchFiltersPage.clickTypeFilterHeader();
|
await searchFiltersPage.clickTypeFilterHeader();
|
||||||
|
|
||||||
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(10);
|
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(13);
|
||||||
|
|
||||||
await navigationBarPage.navigateToContentServices();
|
await navigationBarPage.navigateToContentServices();
|
||||||
|
|
||||||
@ -170,7 +170,7 @@ describe('Search Radio Component', () => {
|
|||||||
|
|
||||||
await searchFiltersPage.clickTypeFilterHeader();
|
await searchFiltersPage.clickTypeFilterHeader();
|
||||||
|
|
||||||
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(10);
|
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(13);
|
||||||
|
|
||||||
await navigationBarPage.navigateToContentServices();
|
await navigationBarPage.navigateToContentServices();
|
||||||
jsonFile.categories[5].component.settings.pageSize = 9;
|
jsonFile.categories[5].component.settings.pageSize = 9;
|
||||||
@ -184,7 +184,7 @@ describe('Search Radio Component', () => {
|
|||||||
|
|
||||||
await searchFiltersPage.clickTypeFilterHeader();
|
await searchFiltersPage.clickTypeFilterHeader();
|
||||||
|
|
||||||
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(9);
|
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(12);
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
||||||
@ -213,21 +213,21 @@ describe('Search Radio Component', () => {
|
|||||||
|
|
||||||
await searchFiltersPage.clickTypeFilterHeader();
|
await searchFiltersPage.clickTypeFilterHeader();
|
||||||
|
|
||||||
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(5);
|
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(8);
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().clickShowMoreButton();
|
await searchFiltersPage.typeFiltersPage().clickShowMoreButton();
|
||||||
|
|
||||||
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(10);
|
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(13);
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsNotDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsNotDisplayed();
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsDisplayed();
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().clickShowLessButton();
|
await searchFiltersPage.typeFiltersPage().clickShowLessButton();
|
||||||
|
|
||||||
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(5);
|
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(8);
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
||||||
@ -244,21 +244,21 @@ describe('Search Radio Component', () => {
|
|||||||
|
|
||||||
await searchFiltersPage.clickTypeFilterHeader();
|
await searchFiltersPage.clickTypeFilterHeader();
|
||||||
|
|
||||||
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(5);
|
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(8);
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().clickShowMoreButton();
|
await searchFiltersPage.typeFiltersPage().clickShowMoreButton();
|
||||||
|
|
||||||
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(10);
|
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(13);
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsNotDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsNotDisplayed();
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsDisplayed();
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().clickShowLessButton();
|
await searchFiltersPage.typeFiltersPage().clickShowLessButton();
|
||||||
|
|
||||||
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(5);
|
await expect(await searchFiltersPage.typeFiltersPage().getRadioButtonsNumberOnPage()).toBe(8);
|
||||||
|
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowMoreButtonIsDisplayed();
|
||||||
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
await searchFiltersPage.typeFiltersPage().checkShowLessButtonIsNotDisplayed();
|
||||||
|
@ -176,6 +176,23 @@ export class SearchConfiguration {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'createdModifiedDateRange',
|
||||||
|
name: 'Date',
|
||||||
|
enabled: true,
|
||||||
|
component: {
|
||||||
|
selector: 'date-range-advanced',
|
||||||
|
settings: {
|
||||||
|
dateFormat: 'dd-MMM-yy',
|
||||||
|
maxDate: 'today',
|
||||||
|
field: 'cm:created, cm:modified',
|
||||||
|
displayedLabelsByField: {
|
||||||
|
"cm:created": 'Created Date',
|
||||||
|
"cm:modified": 'Modified Date'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
highlight: {
|
highlight: {
|
||||||
|
@ -415,6 +415,38 @@
|
|||||||
"EXCLUDE_LABEL": "EXCLUDE these words",
|
"EXCLUDE_LABEL": "EXCLUDE these words",
|
||||||
"EXCLUDE_HINT": "Results will exclude matches with these words"
|
"EXCLUDE_HINT": "Results will exclude matches with these words"
|
||||||
},
|
},
|
||||||
|
"DATE_RANGE_ADVANCED": {
|
||||||
|
"OPTIONS": {
|
||||||
|
"ANYTIME": "Anytime",
|
||||||
|
"IN_LAST": "In the last",
|
||||||
|
"BETWEEN": "Between"
|
||||||
|
},
|
||||||
|
"IN_LAST_LABELS": {
|
||||||
|
"DAYS": "Days",
|
||||||
|
"WEEKS": "Weeks",
|
||||||
|
"MONTHS": "Months"
|
||||||
|
},
|
||||||
|
"IN_LAST_DISPLAY_LABELS": {
|
||||||
|
"DAYS": "In the last {{value}} days",
|
||||||
|
"WEEKS": "In the last {{value}} weeks",
|
||||||
|
"MONTHS": "In the last {{value}} months"
|
||||||
|
},
|
||||||
|
"BETWEEN_PLACEHOLDERS": {
|
||||||
|
"START_DATE": "Start Date",
|
||||||
|
"END_DATE": "End Date"
|
||||||
|
},
|
||||||
|
"ERROR": {
|
||||||
|
"IN_LAST": "Value required",
|
||||||
|
"START_DATE": {
|
||||||
|
"REQUIRED": "Start Date required",
|
||||||
|
"INVALID_FORMAT": "Start Date invalid"
|
||||||
|
},
|
||||||
|
"END_DATE": {
|
||||||
|
"REQUIRED": "End Date required",
|
||||||
|
"INVALID_FORMAT": "End Date invalid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"SEARCH_PROPERTIES": {
|
"SEARCH_PROPERTIES": {
|
||||||
"FILE_SIZE": "File Size",
|
"FILE_SIZE": "File Size",
|
||||||
"FILE_SIZE_PLACEHOLDER": "Enter file size",
|
"FILE_SIZE_PLACEHOLDER": "Enter file size",
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
<adf-search-filter-tabbed>
|
||||||
|
<ng-container *ngFor="let field of fields">
|
||||||
|
<adf-search-date-range-advanced
|
||||||
|
*adf-search-filter-tab="settings.displayedLabelsByField[field]"
|
||||||
|
[dateFormat]="settings.dateFormat"
|
||||||
|
[maxDate]="settings.maxDate"
|
||||||
|
[field]="field"
|
||||||
|
[initialValue]="startValue"
|
||||||
|
(changed)="onDateRangedValueChanged($event, field)"
|
||||||
|
(valid)="tabsValidity[field]=$event">
|
||||||
|
</adf-search-date-range-advanced>
|
||||||
|
</ng-container>
|
||||||
|
</adf-search-filter-tabbed>
|
@ -0,0 +1,236 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { SearchDateRangeAdvanced } from './search-date-range-advanced/search-date-range-advanced';
|
||||||
|
import { SearchFilterTabbedComponent } from '../search-filter-tabbed/search-filter-tabbed.component';
|
||||||
|
import { SearchDateRangeAdvancedComponent } from './search-date-range-advanced/search-date-range-advanced.component';
|
||||||
|
import { SearchDateRangeAdvancedTabbedComponent } from './search-date-range-advanced-tabbed.component';
|
||||||
|
import { DateRangeType } from './search-date-range-advanced/date-range-type';
|
||||||
|
import { InLastDateType } from './search-date-range-advanced/in-last-date-type';
|
||||||
|
import {
|
||||||
|
endOfDay,
|
||||||
|
endOfToday,
|
||||||
|
formatISO,
|
||||||
|
parse,
|
||||||
|
startOfDay, startOfMonth,
|
||||||
|
startOfWeek,
|
||||||
|
subDays,
|
||||||
|
subMonths,
|
||||||
|
subWeeks
|
||||||
|
} from 'date-fns';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-search-filter-tabbed',
|
||||||
|
template: ``
|
||||||
|
})
|
||||||
|
export class MockSearchFilterTabbedComponent {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-search-date-range-advanced',
|
||||||
|
template: ``
|
||||||
|
})
|
||||||
|
export class MockSearchDateRangeAdvancedComponent {
|
||||||
|
@Input()
|
||||||
|
dateFormat: string;
|
||||||
|
@Input()
|
||||||
|
maxDate: string;
|
||||||
|
@Input()
|
||||||
|
field: string;
|
||||||
|
@Input()
|
||||||
|
initialValue: SearchDateRangeAdvanced;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changed = new EventEmitter<Partial<SearchDateRangeAdvanced>>();
|
||||||
|
@Output()
|
||||||
|
valid = new EventEmitter<boolean>();
|
||||||
|
}
|
||||||
|
describe('SearchDateRangeAdvancedTabbedComponent', () => {
|
||||||
|
let component: SearchDateRangeAdvancedTabbedComponent;
|
||||||
|
let fixture: ComponentFixture<SearchDateRangeAdvancedTabbedComponent>;
|
||||||
|
let betweenMockData: SearchDateRangeAdvanced;
|
||||||
|
let inLastMockData: SearchDateRangeAdvanced;
|
||||||
|
let anyMockDate: SearchDateRangeAdvanced;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SearchDateRangeAdvancedTabbedComponent, SearchFilterTabbedComponent, SearchDateRangeAdvancedComponent],
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
ContentTestingModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: SearchFilterTabbedComponent, useClass: MockSearchFilterTabbedComponent },
|
||||||
|
{ provide: SearchDateRangeAdvancedComponent, useClass: MockSearchDateRangeAdvancedComponent }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(SearchDateRangeAdvancedTabbedComponent);
|
||||||
|
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.id = 'dateRangeAdvanced';
|
||||||
|
component.context = {
|
||||||
|
queryFragments: {
|
||||||
|
dateRangeAdvanced: ''
|
||||||
|
},
|
||||||
|
update: jasmine.createSpy('update')
|
||||||
|
} as any;
|
||||||
|
component.settings = {
|
||||||
|
hideDefaultAction: false,
|
||||||
|
dateFormat: 'dd-MMM-yy',
|
||||||
|
maxDate: 'today',
|
||||||
|
field: 'createdDate, modifiedDate',
|
||||||
|
displayedLabelsByField: {
|
||||||
|
createdDate: 'Created Date',
|
||||||
|
modifiedDate: 'Modified Date'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
component.tabsValidity = {
|
||||||
|
createdDate: true,
|
||||||
|
modifiedDate: true
|
||||||
|
};
|
||||||
|
|
||||||
|
betweenMockData = {
|
||||||
|
dateRangeType: DateRangeType.BETWEEN,
|
||||||
|
inLastValueType: InLastDateType.DAYS,
|
||||||
|
inLastValue: undefined,
|
||||||
|
betweenStartDate: parse('05-Jun-23', 'dd-MMM-yy', new Date()),
|
||||||
|
betweenEndDate: parse('07-Jun-23', 'dd-MMM-yy', new Date())
|
||||||
|
};
|
||||||
|
inLastMockData = {
|
||||||
|
dateRangeType: DateRangeType.IN_LAST,
|
||||||
|
inLastValueType: InLastDateType.WEEKS,
|
||||||
|
inLastValue: '5',
|
||||||
|
betweenStartDate: undefined,
|
||||||
|
betweenEndDate: undefined
|
||||||
|
};
|
||||||
|
anyMockDate = {
|
||||||
|
dateRangeType: DateRangeType.ANY,
|
||||||
|
inLastValueType: InLastDateType.DAYS,
|
||||||
|
inLastValue: null,
|
||||||
|
betweenStartDate: null,
|
||||||
|
betweenEndDate: null
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to generate separate fields on init', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.fields.length).toBe(2);
|
||||||
|
expect(component.fields).toEqual(['createdDate', 'modifiedDate']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return hasValidValue as false if any of the fields has an invalid value', () => {
|
||||||
|
component.tabsValidity['createdDate'] = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.hasValidValue()).toBeFalse();
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.tabsValidity['modifiedDate'] = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.hasValidValue()).toBeFalse();
|
||||||
|
component.tabsValidity['createdDate'] = true;
|
||||||
|
component.tabsValidity['modifiedDate'] = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.hasValidValue()).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update displayValue when values are submitted', () => {
|
||||||
|
spyOn(component.displayValue$, 'next');
|
||||||
|
component.onDateRangedValueChanged(betweenMockData, 'createdDate');
|
||||||
|
component.onDateRangedValueChanged(inLastMockData, 'modifiedDate');
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.submitValues();
|
||||||
|
expect(component.displayValue$.next).toHaveBeenCalledWith('CREATED DATE: 05-Jun-23 - 07-Jun-23 MODIFIED DATE: SEARCH.DATE_RANGE_ADVANCED.IN_LAST_DISPLAY_LABELS.WEEKS');
|
||||||
|
|
||||||
|
component.onDateRangedValueChanged(anyMockDate, 'createdDate');
|
||||||
|
component.onDateRangedValueChanged(anyMockDate, 'modifiedDate');
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.submitValues();
|
||||||
|
expect(component.displayValue$.next).toHaveBeenCalledWith('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update query when values are changed', () => {
|
||||||
|
component.onDateRangedValueChanged(betweenMockData, 'createdDate');
|
||||||
|
component.onDateRangedValueChanged(inLastMockData, 'modifiedDate');
|
||||||
|
fixture.detectChanges();
|
||||||
|
let inLastStartDate = startOfWeek(subWeeks(new Date(), 5));
|
||||||
|
let query = `createdDate:['${formatISO(startOfDay(betweenMockData.betweenStartDate))}' TO '${formatISO(endOfDay(betweenMockData.betweenEndDate))}']` +
|
||||||
|
` AND modifiedDate:['${formatISO(startOfDay(inLastStartDate))}' TO '${formatISO(endOfToday())}']`;
|
||||||
|
expect(component.combinedQuery).toEqual(query);
|
||||||
|
|
||||||
|
inLastMockData = {
|
||||||
|
dateRangeType: DateRangeType.IN_LAST,
|
||||||
|
inLastValueType: InLastDateType.DAYS,
|
||||||
|
inLastValue: '9',
|
||||||
|
betweenStartDate: null,
|
||||||
|
betweenEndDate: null
|
||||||
|
};
|
||||||
|
component.onDateRangedValueChanged(inLastMockData, 'modifiedDate');
|
||||||
|
fixture.detectChanges();
|
||||||
|
inLastStartDate = startOfDay(subDays(new Date(), 9));
|
||||||
|
query = `createdDate:['${formatISO(startOfDay(betweenMockData.betweenStartDate))}' TO '${formatISO(endOfDay(betweenMockData.betweenEndDate))}']` +
|
||||||
|
` AND modifiedDate:['${formatISO(startOfDay(inLastStartDate))}' TO '${formatISO(endOfToday())}']`;
|
||||||
|
expect(component.combinedQuery).toEqual(query);
|
||||||
|
|
||||||
|
inLastMockData = {
|
||||||
|
dateRangeType: DateRangeType.IN_LAST,
|
||||||
|
inLastValueType: InLastDateType.MONTHS,
|
||||||
|
inLastValue: '7',
|
||||||
|
betweenStartDate: null,
|
||||||
|
betweenEndDate: null
|
||||||
|
};
|
||||||
|
component.onDateRangedValueChanged(inLastMockData, 'modifiedDate');
|
||||||
|
fixture.detectChanges();
|
||||||
|
inLastStartDate = startOfMonth(subMonths(new Date(), 7));
|
||||||
|
query = `createdDate:['${formatISO(startOfDay(betweenMockData.betweenStartDate))}' TO '${formatISO(endOfDay(betweenMockData.betweenEndDate))}']` +
|
||||||
|
` AND modifiedDate:['${formatISO(startOfDay(inLastStartDate))}' TO '${formatISO(endOfToday())}']`;
|
||||||
|
expect(component.combinedQuery).toEqual(query);
|
||||||
|
expect(component.combinedQuery).toEqual(query);
|
||||||
|
|
||||||
|
component.onDateRangedValueChanged(anyMockDate, 'createdDate');
|
||||||
|
component.onDateRangedValueChanged(anyMockDate, 'modifiedDate');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.combinedQuery).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger context.update() when values are submitted', () => {
|
||||||
|
component.onDateRangedValueChanged(betweenMockData, 'createdDate');
|
||||||
|
component.onDateRangedValueChanged(inLastMockData, 'modifiedDate');
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.submitValues();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const inLastStartDate = startOfWeek(subWeeks(new Date(), 5));
|
||||||
|
const query = `createdDate:['${formatISO(startOfDay(betweenMockData.betweenStartDate))}' TO '${formatISO(endOfDay(betweenMockData.betweenEndDate))}']` +
|
||||||
|
` AND modifiedDate:['${formatISO(startOfDay(inLastStartDate))}' TO '${formatISO(endOfToday())}']`;
|
||||||
|
expect(component.context.queryFragments['dateRangeAdvanced']).toEqual(query);
|
||||||
|
expect(component.context.update).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear values and search filter when widget is reset', () => {
|
||||||
|
spyOn(component.displayValue$, 'next');
|
||||||
|
component.reset();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.combinedQuery).toBe('');
|
||||||
|
expect(component.combinedDisplayValue).toBe('');
|
||||||
|
expect(component.displayValue$.next).toHaveBeenCalledWith('');
|
||||||
|
expect(component.context.queryFragments['dateRangeAdvanced']).toEqual('');
|
||||||
|
expect(component.context.update).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,171 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { SearchDateRangeAdvanced } from './search-date-range-advanced/search-date-range-advanced';
|
||||||
|
import { DateRangeType } from './search-date-range-advanced/date-range-type';
|
||||||
|
import { SearchWidget } from '../../models/search-widget.interface';
|
||||||
|
import { SearchWidgetSettings } from '../../models/search-widget-settings.interface';
|
||||||
|
import { SearchQueryBuilderService } from '../../services/search-query-builder.service';
|
||||||
|
import { InLastDateType } from './search-date-range-advanced/in-last-date-type';
|
||||||
|
import { TranslationService } from '@alfresco/adf-core';
|
||||||
|
import {
|
||||||
|
endOfDay,
|
||||||
|
endOfToday,
|
||||||
|
format,
|
||||||
|
formatISO,
|
||||||
|
startOfDay,
|
||||||
|
startOfMonth,
|
||||||
|
startOfWeek,
|
||||||
|
subDays,
|
||||||
|
subMonths,
|
||||||
|
subWeeks
|
||||||
|
} from 'date-fns';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-search-date-range-advanced-tabbed',
|
||||||
|
templateUrl: './search-date-range-advanced-tabbed.component.html',
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class SearchDateRangeAdvancedTabbedComponent implements SearchWidget, OnInit {
|
||||||
|
displayValue$ = new Subject<string>();
|
||||||
|
id: string;
|
||||||
|
startValue: SearchDateRangeAdvanced = {
|
||||||
|
dateRangeType: DateRangeType.ANY,
|
||||||
|
inLastValueType: InLastDateType.DAYS,
|
||||||
|
inLastValue: undefined,
|
||||||
|
betweenStartDate: undefined,
|
||||||
|
betweenEndDate: undefined
|
||||||
|
};
|
||||||
|
settings?: SearchWidgetSettings;
|
||||||
|
context?: SearchQueryBuilderService;
|
||||||
|
fields: string[];
|
||||||
|
tabsValidity: { [key: string]: boolean } = {};
|
||||||
|
combinedQuery: string;
|
||||||
|
combinedDisplayValue: string;
|
||||||
|
|
||||||
|
private value: { [key: string]: Partial<SearchDateRangeAdvanced> } = {};
|
||||||
|
private queryMapByField: Map<string, string> = new Map<string, string>();
|
||||||
|
private displayValueMapByField: Map<string, string> = new Map<string, string>();
|
||||||
|
|
||||||
|
constructor(private translateService: TranslationService) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.fields = this.settings?.field.split(',').map(field => field.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentValue(): { [key: string]: Partial<SearchDateRangeAdvanced> } {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasValidValue(): boolean {
|
||||||
|
return Object.values(this.tabsValidity).every((valid) => valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.combinedQuery = '';
|
||||||
|
this.combinedDisplayValue = '';
|
||||||
|
this.startValue = {
|
||||||
|
...this.startValue
|
||||||
|
};
|
||||||
|
this.submitValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(value: { [key: string]: SearchDateRangeAdvanced }) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
submitValues() {
|
||||||
|
this.context.queryFragments[this.id] = this.combinedQuery;
|
||||||
|
this.displayValue$.next(this.combinedDisplayValue);
|
||||||
|
if (this.id && this.context) {
|
||||||
|
this.context.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onDateRangedValueChanged(value: Partial<SearchDateRangeAdvanced>, field: string) {
|
||||||
|
this.value[field] = value;
|
||||||
|
this.updateQuery(value, field);
|
||||||
|
this.updateDisplayValue(value, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateQuery(value: Partial<SearchDateRangeAdvanced>, field: string): string {
|
||||||
|
let query = '';
|
||||||
|
let startDate: Date;
|
||||||
|
let endDate: Date;
|
||||||
|
if (value.dateRangeType === DateRangeType.IN_LAST) {
|
||||||
|
if (value.inLastValue) {
|
||||||
|
switch(value.inLastValueType) {
|
||||||
|
case InLastDateType.DAYS:
|
||||||
|
startDate = startOfDay(subDays(new Date(), parseInt(value.inLastValue, 10)));
|
||||||
|
break;
|
||||||
|
case InLastDateType.WEEKS:
|
||||||
|
startDate = startOfWeek(subWeeks(new Date(), parseInt(value.inLastValue, 10)));
|
||||||
|
break;
|
||||||
|
case InLastDateType.MONTHS:
|
||||||
|
startDate = startOfMonth(subMonths(new Date(), parseInt(value.inLastValue, 10)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endDate = endOfToday();
|
||||||
|
} else if (value.dateRangeType === DateRangeType.BETWEEN) {
|
||||||
|
if (value.betweenStartDate && value.betweenEndDate) {
|
||||||
|
startDate = startOfDay(value.betweenStartDate);
|
||||||
|
endDate = endOfDay(value.betweenEndDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (startDate && endDate) {
|
||||||
|
query = `${field}:['${formatISO(startDate)}' TO '${formatISO(endDate)}']`;
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateDisplayValue(value: Partial<SearchDateRangeAdvanced>): string {
|
||||||
|
let displayValue = '';
|
||||||
|
if (value.dateRangeType === DateRangeType.IN_LAST && value.inLastValue) {
|
||||||
|
displayValue = this.translateService.instant(`SEARCH.DATE_RANGE_ADVANCED.IN_LAST_DISPLAY_LABELS.${value.inLastValueType}`, {
|
||||||
|
value: value.inLastValue
|
||||||
|
});
|
||||||
|
} else if (value.dateRangeType === DateRangeType.BETWEEN && value.betweenStartDate && value.betweenEndDate) {
|
||||||
|
displayValue = `${format(startOfDay(value.betweenStartDate), this.settings.dateFormat)} - ${format(endOfDay(value.betweenEndDate), this.settings.dateFormat)}`;
|
||||||
|
}
|
||||||
|
return displayValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateQuery(value: Partial<SearchDateRangeAdvanced>, field: string) {
|
||||||
|
this.combinedQuery = '';
|
||||||
|
this.queryMapByField.set(field, this.generateQuery(value, field));
|
||||||
|
this.queryMapByField.forEach((query: string) => {
|
||||||
|
if (query) {
|
||||||
|
this.combinedQuery = this.combinedQuery ? `${this.combinedQuery} AND ${query}` : `${query}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateDisplayValue(value: Partial<SearchDateRangeAdvanced>, field: string) {
|
||||||
|
this.combinedDisplayValue = '';
|
||||||
|
this.displayValueMapByField.set(field, this.generateDisplayValue(value));
|
||||||
|
this.displayValueMapByField.forEach((displayValue: string, fieldForDisplayLabel: string) => {
|
||||||
|
if (displayValue) {
|
||||||
|
const displayLabelForField = `${this.translateService.instant(this.settings.displayedLabelsByField[fieldForDisplayLabel]).toUpperCase()}: ${displayValue}`;
|
||||||
|
this.combinedDisplayValue = this.combinedDisplayValue ? `${this.combinedDisplayValue} ${displayLabelForField}` : `${displayLabelForField}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export enum DateRangeType {
|
||||||
|
ANY = 'ANY',
|
||||||
|
IN_LAST = 'IN_LAST',
|
||||||
|
BETWEEN = 'BETWEEN',
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export enum InLastDateType {
|
||||||
|
DAYS = 'DAYS',
|
||||||
|
WEEKS = 'WEEKS',
|
||||||
|
MONTHS = 'MONTHS'
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
<ng-container [formGroup]="form">
|
||||||
|
<mat-radio-group formControlName="dateRangeType">
|
||||||
|
<span class="adf-search-date-range-horizontal-container">
|
||||||
|
<mat-radio-button [value]="DateRangeType.ANY" data-automation-id="date-range-advanced-anytime">
|
||||||
|
{{ 'SEARCH.DATE_RANGE_ADVANCED.OPTIONS.ANYTIME' | translate }}
|
||||||
|
</mat-radio-button>
|
||||||
|
</span>
|
||||||
|
<span class="adf-search-date-range-horizontal-container">
|
||||||
|
<mat-radio-button [value]="DateRangeType.IN_LAST" data-automation-id="date-range-advanced-in-last">
|
||||||
|
{{ 'SEARCH.DATE_RANGE_ADVANCED.OPTIONS.IN_LAST' | translate }}
|
||||||
|
</mat-radio-button>
|
||||||
|
<mat-form-field class="adf-search-date-range-form-field adf-search-date-range-input-field">
|
||||||
|
<input matInput type="number" min="1" formControlName="inLastValue" data-automation-id="date-range-advanced-in-last-input" (input)="narrowDownAllowedCharacters($event)" (keydown)="preventIncorrectNumberCharacters($event)">
|
||||||
|
<mat-error *ngIf="form.controls.inLastValue.errors?.required">{{ 'SEARCH.DATE_RANGE_ADVANCED.ERROR.IN_LAST' | translate }}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="adf-search-date-range-form-field">
|
||||||
|
<mat-select formControlName="inLastValueType" data-automation-id="date-range-advanced-in-last-dropdown">
|
||||||
|
<mat-option data-automation-id="date-range-advanced-in-last-option-days" [value]="InLastDateType.DAYS">{{ 'SEARCH.DATE_RANGE_ADVANCED.IN_LAST_LABELS.DAYS' | translate }}</mat-option>
|
||||||
|
<mat-option data-automation-id="date-range-advanced-in-last-option-weeks" [value]="InLastDateType.WEEKS">{{ 'SEARCH.DATE_RANGE_ADVANCED.IN_LAST_LABELS.WEEKS' | translate }}</mat-option>
|
||||||
|
<mat-option data-automation-id="date-range-advanced-in-last-option-months" [value]="InLastDateType.MONTHS">{{ 'SEARCH.DATE_RANGE_ADVANCED.IN_LAST_LABELS.MONTHS' | translate }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</span>
|
||||||
|
<span class="adf-search-date-range-horizontal-container">
|
||||||
|
<mat-radio-button [value]="DateRangeType.BETWEEN" data-automation-id="date-range-advanced-between">
|
||||||
|
{{ 'SEARCH.DATE_RANGE_ADVANCED.OPTIONS.BETWEEN' | translate }}
|
||||||
|
</mat-radio-button>
|
||||||
|
<mat-form-field class="adf-search-date-range-form-field">
|
||||||
|
<mat-date-range-input [rangePicker]="$any(picker)" [max]="convertedMaxDate">
|
||||||
|
<input matStartDate placeholder="{{ 'SEARCH.DATE_RANGE_ADVANCED.BETWEEN_PLACEHOLDERS.START_DATE' | translate }}"
|
||||||
|
data-automation-id="date-range-advanced-between-start-input" [formControl]="betweenStartDateFormControl" (change)="dateChanged($event, betweenStartDateFormControl)">
|
||||||
|
<input matEndDate placeholder="{{ 'SEARCH.DATE_RANGE_ADVANCED.BETWEEN_PLACEHOLDERS.END_DATE' | translate }}"
|
||||||
|
data-automation-id="date-range-advanced-between-end-input" [formControl]="betweenEndDateFormControl" (change)="dateChanged($event, betweenEndDateFormControl)">
|
||||||
|
</mat-date-range-input>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="picker" data-automation-id="date-range-advanced-between-datepicker-toggle"></mat-datepicker-toggle>
|
||||||
|
<mat-date-range-picker #picker></mat-date-range-picker>
|
||||||
|
|
||||||
|
<mat-error *ngIf="betweenStartDateFormControl.errors?.invalidDate">{{ 'SEARCH.DATE_RANGE_ADVANCED.ERROR.START_DATE.INVALID_FORMAT' | translate }}</mat-error>
|
||||||
|
<mat-error *ngIf="betweenStartDateFormControl.errors?.required">{{ 'SEARCH.DATE_RANGE_ADVANCED.ERROR.START_DATE.REQUIRED' | translate }}</mat-error>
|
||||||
|
<mat-error *ngIf="betweenEndDateFormControl.errors?.invalidDate">{{ 'SEARCH.DATE_RANGE_ADVANCED.ERROR.END_DATE.INVALID_FORMAT' | translate }}</mat-error>
|
||||||
|
<mat-error *ngIf="betweenEndDateFormControl.errors?.required">{{ 'SEARCH.DATE_RANGE_ADVANCED.ERROR.END_DATE.REQUIRED' | translate }}</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</span>
|
||||||
|
</mat-radio-group>
|
||||||
|
</ng-container>
|
@ -0,0 +1,45 @@
|
|||||||
|
.adf-search-date-range-advanced {
|
||||||
|
.mat-radio-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-radio-button {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-search-date-range-horizontal-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
|
||||||
|
.adf-search-date-range-input-field {
|
||||||
|
width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-search-date-range-form-field {
|
||||||
|
padding-left: 10px;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.mat-form-field-wrapper {
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-bottom: 1.25em;
|
||||||
|
border: 1px solid var(--adf-theme-mat-grey-color-a400);
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
.mat-form-field-infix {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field-underline {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field-subscript-wrapper {
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,281 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ContentTestingModule } from '../../../../testing/content.testing.module';
|
||||||
|
import { SearchDateRangeAdvancedComponent } from './search-date-range-advanced.component';
|
||||||
|
import { addDays, endOfToday, format, parse, startOfYesterday, subDays } from 'date-fns';
|
||||||
|
import { Validators } from '@angular/forms';
|
||||||
|
|
||||||
|
describe('SearchDateRangeAdvancedComponent', () => {
|
||||||
|
let component: SearchDateRangeAdvancedComponent;
|
||||||
|
let fixture: ComponentFixture<SearchDateRangeAdvancedComponent>;
|
||||||
|
|
||||||
|
const startDateSampleValue = parse('05-Jun-23', 'dd-MMM-yy', new Date());
|
||||||
|
const endDateSampleValue = parse('07-Jun-23', 'dd-MMM-yy', new Date());
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SearchDateRangeAdvancedComponent],
|
||||||
|
imports: [
|
||||||
|
TranslateModule.forRoot(),
|
||||||
|
ContentTestingModule
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(SearchDateRangeAdvancedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.field = 'test-field';
|
||||||
|
component.dateFormat = 'dd-MMM-yy';
|
||||||
|
component.maxDate = 'today';
|
||||||
|
component.form.setValue({
|
||||||
|
dateRangeType: component.DateRangeType.ANY,
|
||||||
|
inLastValueType: component.InLastDateType.DAYS,
|
||||||
|
inLastValue: null,
|
||||||
|
betweenStartDate: null,
|
||||||
|
betweenEndDate: null
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getElementBySelector = (selector: string) => fixture.debugElement.query(By.css(selector)).nativeElement;
|
||||||
|
|
||||||
|
const enterValueInInputFieldAndTriggerEvent = (inputElementId: string, value: string, event = 'input') => {
|
||||||
|
const inputField = getElementBySelector(`[data-automation-id="${inputElementId}"]`);
|
||||||
|
inputField.value = value;
|
||||||
|
inputField.dispatchEvent(new Event(event));
|
||||||
|
fixture.detectChanges();
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectDropdownOption = (itemId: string) => {
|
||||||
|
const matSelect = fixture.debugElement.query(By.css('[data-automation-id="date-range-advanced-in-last-dropdown"]')).nativeElement;
|
||||||
|
matSelect.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
const matOption = fixture.debugElement.query(By.css(`[data-automation-id="${itemId}"]`)).nativeElement;
|
||||||
|
matOption.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should set values if initial value is provided', () => {
|
||||||
|
let value: any = {
|
||||||
|
dateRangeType: component.DateRangeType.ANY
|
||||||
|
};
|
||||||
|
component.initialValue = value;
|
||||||
|
component.ngOnInit();
|
||||||
|
expect(component.form.controls.dateRangeType.value).toEqual(component.DateRangeType.ANY);
|
||||||
|
|
||||||
|
value = {
|
||||||
|
dateRangeType: component.DateRangeType.IN_LAST,
|
||||||
|
inLastValueType: component.InLastDateType.WEEKS,
|
||||||
|
inLastValue: '5'
|
||||||
|
};
|
||||||
|
component.initialValue = value;
|
||||||
|
component.ngOnInit();
|
||||||
|
expect(component.form.controls.dateRangeType.value).toEqual(component.DateRangeType.IN_LAST);
|
||||||
|
expect(component.form.controls.inLastValueType.value).toEqual(component.InLastDateType.WEEKS);
|
||||||
|
expect(component.form.controls.inLastValue.value).toEqual('5');
|
||||||
|
|
||||||
|
value = {
|
||||||
|
dateRangeType: component.DateRangeType.BETWEEN,
|
||||||
|
betweenStartDate: startOfYesterday(),
|
||||||
|
betweenEndDate: endOfToday()
|
||||||
|
};
|
||||||
|
component.initialValue = value;
|
||||||
|
component.ngOnInit();
|
||||||
|
expect(component.form.controls.dateRangeType.value).toEqual(component.DateRangeType.BETWEEN);
|
||||||
|
expect(component.form.controls.betweenStartDate.value).toEqual(startOfYesterday());
|
||||||
|
expect(component.form.controls.betweenEndDate.value).toEqual(endOfToday());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not have any validators on any input fields when anytime option is selected', () => {
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.ANY);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.form.controls.inLastValue.validator).toBeNull();
|
||||||
|
expect(component.form.controls.betweenStartDate.validator).toBeNull();
|
||||||
|
expect(component.form.controls.betweenEndDate.validator).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the required validator on in last input field and remove validators from between input fields when In the last option is selected', () => {
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.IN_LAST);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.form.controls.inLastValue.hasValidator(Validators.required)).toBeTrue();
|
||||||
|
expect(component.form.controls.betweenStartDate.validator).toBeNull();
|
||||||
|
expect(component.form.controls.betweenEndDate.validator).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set the validators on in between input fields and remove validator from in last input fields when Between option is selected', () => {
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.BETWEEN);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.form.controls.betweenStartDate.hasValidator(Validators.required)).toBeTrue();
|
||||||
|
expect(component.form.controls.betweenEndDate.hasValidator(Validators.required)).toBeTrue();
|
||||||
|
expect(component.form.controls.betweenEndDate.hasValidator(component.endDateValidator)).toBeTrue();
|
||||||
|
expect(component.form.controls.inLastValue.validator).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be able to set zero or negative values in In the last input field', () => {
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.IN_LAST);
|
||||||
|
fixture.detectChanges();
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-in-last-input', '-5');
|
||||||
|
let inLastInputFieldValue = getElementBySelector('[data-automation-id="date-range-advanced-in-last-input"]').value;
|
||||||
|
expect(inLastInputFieldValue).toBe('5');
|
||||||
|
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-in-last-input', '0');
|
||||||
|
inLastInputFieldValue = getElementBySelector('[data-automation-id="date-range-advanced-in-last-input"]').value;
|
||||||
|
expect(inLastInputFieldValue).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should give an invalid date error when manually setting a start date and an end date that are not in the correct format', () => {
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.BETWEEN);
|
||||||
|
fixture.detectChanges();
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-between-start-input', 'invalid-date-input', 'change');
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-between-end-input', 'invalid-date-input', 'change');
|
||||||
|
expect(component.form.controls.betweenStartDate.errors.invalidDate).toBeTrue();
|
||||||
|
expect(component.form.controls.betweenEndDate.errors.invalidDate).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should give an invalid date error when manually setting a start Date that is after the end date', () => {
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.BETWEEN);
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.form.controls.betweenEndDate.setValue(new Date());
|
||||||
|
const startDate = format(addDays(component.form.controls.betweenEndDate.value, 3), component.dateFormat);
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-between-start-input', startDate, 'change');
|
||||||
|
expect(component.form.controls.betweenEndDate.errors.invalidDate).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should give an invalid date error when manually setting an end Date that is before the start date', () => {
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.BETWEEN);
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.form.controls.betweenStartDate.setValue(new Date());
|
||||||
|
const endDate = format(subDays(component.form.controls.betweenStartDate.value, 3), component.dateFormat);
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-between-end-input', endDate, 'change');
|
||||||
|
expect(component.form.controls.betweenEndDate.errors.invalidDate).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should give an invalid date error when setting an endDate that is after the max date', () => {
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.BETWEEN);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const endDate = format(addDays(component.convertedMaxDate, 3), component.dateFormat);
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-between-end-input', endDate, 'change');
|
||||||
|
expect(component.form.controls.betweenEndDate.errors.invalidDate).toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit valid as false when form is invalid', () => {
|
||||||
|
spyOn(component.valid, 'emit');
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.IN_LAST);
|
||||||
|
fixture.detectChanges();
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-in-last-input', '');
|
||||||
|
selectDropdownOption('date-range-advanced-in-last-option-weeks');
|
||||||
|
expect(component.valid.emit).toHaveBeenCalledWith(false);
|
||||||
|
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.BETWEEN);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.valid.emit).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit valid as true when form is valid', () => {
|
||||||
|
spyOn(component.valid, 'emit');
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.IN_LAST);
|
||||||
|
fixture.detectChanges();
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-in-last-input', '5');
|
||||||
|
selectDropdownOption('date-range-advanced-in-last-option-weeks');
|
||||||
|
expect(component.valid.emit).toHaveBeenCalledWith(true);
|
||||||
|
|
||||||
|
component.form.controls.dateRangeType.setValue(component.DateRangeType.BETWEEN);
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.betweenStartDateFormControl.setValue(startDateSampleValue);
|
||||||
|
component.betweenEndDateFormControl.setValue(endDateSampleValue);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.valid.emit).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not emit values when form is invalid', () => {
|
||||||
|
spyOn(component.changed, 'emit');
|
||||||
|
let value = {
|
||||||
|
dateRangeType: component.DateRangeType.IN_LAST,
|
||||||
|
inLastValueType: component.InLastDateType.WEEKS,
|
||||||
|
inLastValue: '',
|
||||||
|
betweenStartDate: undefined,
|
||||||
|
betweenEndDate: undefined
|
||||||
|
};
|
||||||
|
let dateRangeTypeRadioButton = getElementBySelector('[data-automation-id="date-range-advanced-in-last"] .mat-radio-input');
|
||||||
|
dateRangeTypeRadioButton.click();
|
||||||
|
selectDropdownOption('date-range-advanced-in-last-option-weeks');
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-in-last-input', '');
|
||||||
|
expect(component.changed.emit).not.toHaveBeenCalledWith(value);
|
||||||
|
|
||||||
|
component.form.patchValue({
|
||||||
|
dateRangeType: component.DateRangeType.ANY,
|
||||||
|
inLastValueType: component.InLastDateType.DAYS,
|
||||||
|
inLastValue: undefined,
|
||||||
|
betweenStartDate: undefined,
|
||||||
|
betweenEndDate: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
value = {
|
||||||
|
dateRangeType: component.DateRangeType.BETWEEN,
|
||||||
|
inLastValueType: component.InLastDateType.DAYS,
|
||||||
|
inLastValue: undefined,
|
||||||
|
betweenStartDate: '',
|
||||||
|
betweenEndDate: ''
|
||||||
|
};
|
||||||
|
dateRangeTypeRadioButton = getElementBySelector('[data-automation-id="date-range-advanced-between"] .mat-radio-input');
|
||||||
|
dateRangeTypeRadioButton.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.changed.emit).not.toHaveBeenCalledWith(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit values when form is valid', () => {
|
||||||
|
spyOn(component.changed, 'emit');
|
||||||
|
let value = {
|
||||||
|
dateRangeType: component.DateRangeType.IN_LAST,
|
||||||
|
inLastValueType: component.InLastDateType.WEEKS,
|
||||||
|
inLastValue: 5,
|
||||||
|
betweenStartDate: null,
|
||||||
|
betweenEndDate: null
|
||||||
|
};
|
||||||
|
let dateRangeTypeRadioButton = getElementBySelector('[data-automation-id="date-range-advanced-in-last"] .mat-radio-input');
|
||||||
|
dateRangeTypeRadioButton.click();
|
||||||
|
selectDropdownOption('date-range-advanced-in-last-option-weeks');
|
||||||
|
enterValueInInputFieldAndTriggerEvent('date-range-advanced-in-last-input', '5');
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.changed.emit).toHaveBeenCalledWith(value);
|
||||||
|
|
||||||
|
component.form.patchValue({
|
||||||
|
dateRangeType: component.DateRangeType.ANY,
|
||||||
|
inLastValueType: component.InLastDateType.DAYS,
|
||||||
|
inLastValue: undefined,
|
||||||
|
betweenStartDate: undefined,
|
||||||
|
betweenEndDate: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
value = {
|
||||||
|
dateRangeType: component.DateRangeType.BETWEEN,
|
||||||
|
inLastValueType: component.InLastDateType.DAYS,
|
||||||
|
inLastValue: undefined,
|
||||||
|
betweenStartDate: startDateSampleValue,
|
||||||
|
betweenEndDate: endDateSampleValue
|
||||||
|
};
|
||||||
|
dateRangeTypeRadioButton = getElementBySelector('[data-automation-id="date-range-advanced-between"] .mat-radio-input');
|
||||||
|
dateRangeTypeRadioButton.click();
|
||||||
|
component.betweenStartDateFormControl.setValue(startDateSampleValue);
|
||||||
|
component.betweenEndDateFormControl.setValue(endDateSampleValue);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.changed.emit).toHaveBeenCalledWith(value);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,180 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { endOfDay, parse, isValid, isBefore, isAfter } from 'date-fns';
|
||||||
|
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE, MatDateFormats } from '@angular/material/core';
|
||||||
|
import { DateFnsAdapter, MAT_DATE_FNS_FORMATS } from '@angular/material-date-fns-adapter';
|
||||||
|
import { InLastDateType } from './in-last-date-type';
|
||||||
|
import { DateRangeType } from './date-range-type';
|
||||||
|
import { SearchDateRangeAdvanced } from './search-date-range-advanced';
|
||||||
|
import { FormBuilder, UntypedFormControl, Validators } from '@angular/forms';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { UserPreferencesService, UserPreferenceValues, DateFnsUtils } from '@alfresco/adf-core';
|
||||||
|
|
||||||
|
const DEFAULT_DATE_DISPLAY_FORMAT = 'dd-MMM-yy';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-search-date-range-advanced',
|
||||||
|
templateUrl: './search-date-range-advanced.component.html',
|
||||||
|
styleUrls: ['./search-date-range-advanced.component.scss'],
|
||||||
|
providers: [
|
||||||
|
{ provide: DateAdapter, useClass: DateFnsAdapter, deps: [ MAT_DATE_LOCALE ] },
|
||||||
|
{ provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS }
|
||||||
|
],
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
host: {class: 'adf-search-date-range-advanced'}
|
||||||
|
})
|
||||||
|
export class SearchDateRangeAdvancedComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
dateFormat = DEFAULT_DATE_DISPLAY_FORMAT;
|
||||||
|
@Input()
|
||||||
|
maxDate: string;
|
||||||
|
@Input()
|
||||||
|
field: string;
|
||||||
|
@Input()
|
||||||
|
set initialValue(value: SearchDateRangeAdvanced) {
|
||||||
|
if (value) {
|
||||||
|
this.form.patchValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changed = new EventEmitter<Partial<SearchDateRangeAdvanced>>();
|
||||||
|
@Output()
|
||||||
|
valid = new EventEmitter<boolean>();
|
||||||
|
|
||||||
|
form = this.formBuilder.group<SearchDateRangeAdvanced>({
|
||||||
|
dateRangeType: DateRangeType.ANY,
|
||||||
|
inLastValueType: InLastDateType.DAYS,
|
||||||
|
inLastValue: undefined,
|
||||||
|
betweenStartDate: undefined,
|
||||||
|
betweenEndDate: undefined
|
||||||
|
});
|
||||||
|
betweenStartDateFormControl = this.form.controls.betweenStartDate;
|
||||||
|
betweenEndDateFormControl = this.form.controls.betweenEndDate;
|
||||||
|
convertedMaxDate: Date;
|
||||||
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
|
readonly DateRangeType = DateRangeType;
|
||||||
|
readonly InLastDateType = InLastDateType;
|
||||||
|
|
||||||
|
constructor(private formBuilder: FormBuilder,
|
||||||
|
private userPreferencesService: UserPreferencesService,
|
||||||
|
private dateAdapter: DateAdapter<DateFnsAdapter>,
|
||||||
|
@Inject(MAT_DATE_FORMATS) private dateFormatConfig: MatDateFormats) {}
|
||||||
|
|
||||||
|
readonly endDateValidator = (formControl: UntypedFormControl): ({ [key: string]: boolean } | null) => {
|
||||||
|
if (isBefore(formControl.value, this.betweenStartDateFormControl.value) || isAfter(formControl.value, this.convertedMaxDate)) {
|
||||||
|
return {
|
||||||
|
invalidDate: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.dateFormatConfig.display.dateInput = this.dateFormat;
|
||||||
|
this.convertedMaxDate = endOfDay(this.maxDate && this.maxDate !== 'today' ?
|
||||||
|
parse(this.maxDate, this.dateFormat, new Date()) : new Date());
|
||||||
|
this.userPreferencesService
|
||||||
|
.select(UserPreferenceValues.Locale)
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe(locale => this.dateAdapter.setLocale(DateFnsUtils.getLocaleFromString(locale)));
|
||||||
|
this.form.controls.dateRangeType.valueChanges.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe((dateRangeType) => this.updateValidators(dateRangeType));
|
||||||
|
this.form.valueChanges.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe(() => this.onChange());
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateValidators(dateRangeType: DateRangeType) {
|
||||||
|
switch(dateRangeType) {
|
||||||
|
case DateRangeType.BETWEEN:
|
||||||
|
this.betweenStartDateFormControl.setValidators(Validators.required);
|
||||||
|
this.betweenEndDateFormControl.setValidators([Validators.required, this.endDateValidator]);
|
||||||
|
this.form.controls.inLastValue.clearValidators();
|
||||||
|
break;
|
||||||
|
case DateRangeType.IN_LAST:
|
||||||
|
this.form.controls.inLastValue.setValidators(Validators.required);
|
||||||
|
this.betweenStartDateFormControl.clearValidators();
|
||||||
|
this.betweenEndDateFormControl.clearValidators();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.form.controls.inLastValue.clearValidators();
|
||||||
|
this.betweenStartDateFormControl.clearValidators();
|
||||||
|
this.betweenEndDateFormControl.clearValidators();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.betweenStartDateFormControl.updateValueAndValidity();
|
||||||
|
this.betweenEndDateFormControl.updateValueAndValidity();
|
||||||
|
this.form.controls.inLastValue.updateValueAndValidity();
|
||||||
|
}
|
||||||
|
|
||||||
|
private onChange(): void {
|
||||||
|
if (this.form.valid) {
|
||||||
|
this.changed.emit(this.form.value);
|
||||||
|
}
|
||||||
|
this.valid.emit(this.form.valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
dateChanged(event: Event, formControl: UntypedFormControl) {
|
||||||
|
if (event?.target['value']?.trim()) {
|
||||||
|
const date = parse(event.target['value'], this.dateFormat, new Date());
|
||||||
|
if(!isValid(date)) {
|
||||||
|
formControl.setErrors({
|
||||||
|
...formControl.errors,
|
||||||
|
required: false,
|
||||||
|
invalidDate: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
formControl.setErrors({
|
||||||
|
...formControl.errors,
|
||||||
|
invalidDate: false
|
||||||
|
});
|
||||||
|
formControl.setValue(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
narrowDownAllowedCharacters(event: Event) {
|
||||||
|
if (parseInt((event.target as HTMLInputElement).value, 10) === 0) {
|
||||||
|
(event.target as HTMLInputElement).value = '';
|
||||||
|
} else {
|
||||||
|
(event.target as HTMLInputElement).value = (event.target as HTMLInputElement).value.replace(/\D/g, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preventIncorrectNumberCharacters(event: KeyboardEvent): boolean {
|
||||||
|
switch(event.key) {
|
||||||
|
case '.':
|
||||||
|
case '-':
|
||||||
|
case 'e':
|
||||||
|
case '+':
|
||||||
|
return false;
|
||||||
|
case '0':
|
||||||
|
return !!(event.target as HTMLInputElement).value;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DateRangeType } from './date-range-type';
|
||||||
|
import { InLastDateType } from './in-last-date-type';
|
||||||
|
|
||||||
|
export interface SearchDateRangeAdvanced {
|
||||||
|
dateRangeType: DateRangeType;
|
||||||
|
inLastValueType?: InLastDateType;
|
||||||
|
inLastValue?: string;
|
||||||
|
betweenStartDate?: Date;
|
||||||
|
betweenEndDate?: Date;
|
||||||
|
}
|
@ -19,7 +19,7 @@
|
|||||||
<mat-icon>{{ chipIcon }}</mat-icon>
|
<mat-icon>{{ chipIcon }}</mat-icon>
|
||||||
</mat-chip>
|
</mat-chip>
|
||||||
|
|
||||||
<mat-menu #menu="matMenu" backdropClass="adf-search-filter-chip-menu" [class]="'adf-search-filter-chip-menu-panel-' + category.id" (closed)="onClosed()">
|
<mat-menu #menu="matMenu" class="adf-search-widget-extra-width" backdropClass="adf-search-filter-chip-menu" [class]="'adf-search-filter-chip-menu-panel-' + category.id" (closed)="onClosed()">
|
||||||
<div #menuContainer [attr.data-automation-id]="'search-field-' + category.name">
|
<div #menuContainer [attr.data-automation-id]="'search-field-' + category.name">
|
||||||
<adf-search-filter-menu-card (click)="$event.stopPropagation()"
|
<adf-search-filter-menu-card (click)="$event.stopPropagation()"
|
||||||
(keydown.tab)="$event.stopPropagation();"
|
(keydown.tab)="$event.stopPropagation();"
|
||||||
|
@ -24,6 +24,13 @@ import { SearchWidgetContainerComponent } from '../../search-widget-container/se
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-search-widget-chip',
|
selector: 'adf-search-widget-chip',
|
||||||
templateUrl: './search-widget-chip.component.html',
|
templateUrl: './search-widget-chip.component.html',
|
||||||
|
styles: [
|
||||||
|
`
|
||||||
|
.adf-search-widget-extra-width {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
],
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class SearchWidgetChipComponent {
|
export class SearchWidgetChipComponent {
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Directive, Input, TemplateRef } from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[adf-search-filter-tab]'
|
||||||
|
})
|
||||||
|
export class SearchFilterTabDirective {
|
||||||
|
@Input('adf-search-filter-tab')
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
constructor(public readonly templateRef: TemplateRef<any>) { }
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
<mat-tab-group>
|
||||||
|
<mat-tab *ngFor="let tabContent of tabsContents" label="{{tabContent.name | translate}}">
|
||||||
|
<ng-container *ngTemplateOutlet="tabContent.templateRef"></ng-container>
|
||||||
|
</mat-tab>
|
||||||
|
</mat-tab-group>
|
@ -0,0 +1,20 @@
|
|||||||
|
adf-search-filter-tabbed {
|
||||||
|
.mat-tab-label {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&.mat-tab-label-active {
|
||||||
|
border-bottom: 2px solid var(--theme-primary-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The important tag is used here as a workaround for a bug in angular material, when MatTabs are used in conjunction with MatMenu
|
||||||
|
// https://github.com/angular/components/issues/27426
|
||||||
|
.mat-tab-body.mat-tab-body-active .mat-tab-body-content {
|
||||||
|
display: block;
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-ink-bar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Component, ContentChildren, QueryList, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { SearchFilterTabDirective } from './search-filter-tab.directive';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'adf-search-filter-tabbed',
|
||||||
|
templateUrl: './search-filter-tabbed.component.html',
|
||||||
|
styleUrls: ['./search-filter-tabbed.component.scss'],
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class SearchFilterTabbedComponent {
|
||||||
|
@ContentChildren(SearchFilterTabDirective)
|
||||||
|
tabsContents: QueryList<SearchFilterTabDirective>;
|
||||||
|
}
|
@ -34,6 +34,7 @@ export * from './services/search-facet-filters.service';
|
|||||||
export * from './services/search-filter.service';
|
export * from './services/search-filter.service';
|
||||||
export * from './services/search.service';
|
export * from './services/search.service';
|
||||||
export * from './services/search-configuration.service';
|
export * from './services/search-configuration.service';
|
||||||
|
export * from './services/base-query-builder.service';
|
||||||
|
|
||||||
export * from './mocks/search.service.mock';
|
export * from './mocks/search.service.mock';
|
||||||
|
|
||||||
@ -61,6 +62,8 @@ export * from './components/search-filter-chips/search-filter-chips.component';
|
|||||||
export * from './components/search-filter-chips/search-filter-menu-card/search-filter-menu-card.component';
|
export * from './components/search-filter-chips/search-filter-menu-card/search-filter-menu-card.component';
|
||||||
export * from './components/search-facet-field/search-facet-field.component';
|
export * from './components/search-facet-field/search-facet-field.component';
|
||||||
export * from './components/search-logical-filter/search-logical-filter.component';
|
export * from './components/search-logical-filter/search-logical-filter.component';
|
||||||
|
export * from './components/search-date-range-advanced-tabbed/search-date-range-advanced/search-date-range-advanced.component';
|
||||||
|
export * from './components/search-filter-tabbed/search-filter-tabbed.component';
|
||||||
export * from './components/reset-search.directive';
|
export * from './components/reset-search.directive';
|
||||||
export * from './components/search-chip-autocomplete-input/search-chip-autocomplete-input.component';
|
export * from './components/search-chip-autocomplete-input/search-chip-autocomplete-input.component';
|
||||||
export * from './components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component';
|
export * from './components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component';
|
||||||
|
@ -52,6 +52,10 @@ import { SearchFacetChipComponent } from './components/search-filter-chips/searc
|
|||||||
import { SearchLogicalFilterComponent } from './components/search-logical-filter/search-logical-filter.component';
|
import { SearchLogicalFilterComponent } from './components/search-logical-filter/search-logical-filter.component';
|
||||||
import { ResetSearchDirective } from './components/reset-search.directive';
|
import { ResetSearchDirective } from './components/reset-search.directive';
|
||||||
import { SearchPropertiesComponent } from './components/search-properties/search-properties.component';
|
import { SearchPropertiesComponent } from './components/search-properties/search-properties.component';
|
||||||
|
import { SearchFilterTabbedComponent } from './components/search-filter-tabbed/search-filter-tabbed.component';
|
||||||
|
import { SearchDateRangeAdvancedComponent } from './components/search-date-range-advanced-tabbed/search-date-range-advanced/search-date-range-advanced.component';
|
||||||
|
import { SearchDateRangeAdvancedTabbedComponent } from './components/search-date-range-advanced-tabbed/search-date-range-advanced-tabbed.component';
|
||||||
|
import { SearchFilterTabDirective } from './components/search-filter-tabbed/search-filter-tab.directive';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -90,7 +94,11 @@ import { SearchPropertiesComponent } from './components/search-properties/search
|
|||||||
SearchFacetChipComponent,
|
SearchFacetChipComponent,
|
||||||
SearchLogicalFilterComponent,
|
SearchLogicalFilterComponent,
|
||||||
ResetSearchDirective,
|
ResetSearchDirective,
|
||||||
SearchPropertiesComponent
|
SearchPropertiesComponent,
|
||||||
|
SearchFilterTabbedComponent,
|
||||||
|
SearchDateRangeAdvancedComponent,
|
||||||
|
SearchDateRangeAdvancedTabbedComponent,
|
||||||
|
SearchFilterTabDirective
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
@ -116,6 +124,8 @@ import { SearchPropertiesComponent } from './components/search-properties/search
|
|||||||
SearchFilterMenuCardComponent,
|
SearchFilterMenuCardComponent,
|
||||||
SearchFacetFieldComponent,
|
SearchFacetFieldComponent,
|
||||||
SearchLogicalFilterComponent,
|
SearchLogicalFilterComponent,
|
||||||
|
SearchFilterTabbedComponent,
|
||||||
|
SearchDateRangeAdvancedComponent,
|
||||||
ResetSearchDirective
|
ResetSearchDirective
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
|
@ -26,6 +26,9 @@ import { SearchDatetimeRangeComponent } from '../components/search-datetime-rang
|
|||||||
import { SearchLogicalFilterComponent } from '../components/search-logical-filter/search-logical-filter.component';
|
import { SearchLogicalFilterComponent } from '../components/search-logical-filter/search-logical-filter.component';
|
||||||
import { SearchFilterAutocompleteChipsComponent } from '../components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component';
|
import { SearchFilterAutocompleteChipsComponent } from '../components/search-filter-autocomplete-chips/search-filter-autocomplete-chips.component';
|
||||||
import { SearchPropertiesComponent } from '../components/search-properties/search-properties.component';
|
import { SearchPropertiesComponent } from '../components/search-properties/search-properties.component';
|
||||||
|
import {
|
||||||
|
SearchDateRangeAdvancedTabbedComponent
|
||||||
|
} from '../components/search-date-range-advanced-tabbed/search-date-range-advanced-tabbed.component';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -45,7 +48,8 @@ export class SearchFilterService {
|
|||||||
'date-range': SearchDateRangeComponent,
|
'date-range': SearchDateRangeComponent,
|
||||||
'datetime-range': SearchDatetimeRangeComponent,
|
'datetime-range': SearchDatetimeRangeComponent,
|
||||||
'logical-filter': SearchLogicalFilterComponent,
|
'logical-filter': SearchLogicalFilterComponent,
|
||||||
'autocomplete-chips': SearchFilterAutocompleteChipsComponent
|
'autocomplete-chips': SearchFilterAutocompleteChipsComponent,
|
||||||
|
'date-range-advanced': SearchDateRangeAdvancedTabbedComponent
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
81
lib/core/src/lib/common/utils/date-fns-utils.ts
Normal file
81
lib/core/src/lib/common/utils/date-fns-utils.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {ar, cs, da, de, enUS, es, fi, fr, it, ja, nb, nl, pl, ptBR, ru, sv, zhCN} from 'date-fns/locale';
|
||||||
|
|
||||||
|
export class DateFnsUtils {
|
||||||
|
static getLocaleFromString(locale: string): Locale {
|
||||||
|
let dateFnsLocale: Locale;
|
||||||
|
switch(locale) {
|
||||||
|
case 'ar':
|
||||||
|
dateFnsLocale = ar;
|
||||||
|
break;
|
||||||
|
case 'cs':
|
||||||
|
dateFnsLocale = cs;
|
||||||
|
break;
|
||||||
|
case 'da':
|
||||||
|
dateFnsLocale = da;
|
||||||
|
break;
|
||||||
|
case 'de':
|
||||||
|
dateFnsLocale = de;
|
||||||
|
break;
|
||||||
|
case 'en':
|
||||||
|
dateFnsLocale = enUS;
|
||||||
|
break;
|
||||||
|
case 'es':
|
||||||
|
dateFnsLocale = es;
|
||||||
|
break;
|
||||||
|
case 'fi':
|
||||||
|
dateFnsLocale = fi;
|
||||||
|
break;
|
||||||
|
case 'fr':
|
||||||
|
dateFnsLocale = fr;
|
||||||
|
break;
|
||||||
|
case 'it':
|
||||||
|
dateFnsLocale = it;
|
||||||
|
break;
|
||||||
|
case 'ja':
|
||||||
|
dateFnsLocale = ja;
|
||||||
|
break;
|
||||||
|
case 'nb':
|
||||||
|
dateFnsLocale = nb;
|
||||||
|
break;
|
||||||
|
case 'nl':
|
||||||
|
dateFnsLocale = nl;
|
||||||
|
break;
|
||||||
|
case 'pl':
|
||||||
|
dateFnsLocale = pl;
|
||||||
|
break;
|
||||||
|
case 'pt-BR':
|
||||||
|
dateFnsLocale = ptBR;
|
||||||
|
break;
|
||||||
|
case 'ru':
|
||||||
|
dateFnsLocale = ru;
|
||||||
|
break;
|
||||||
|
case 'sv':
|
||||||
|
dateFnsLocale = sv;
|
||||||
|
break;
|
||||||
|
case 'zh-CN':
|
||||||
|
dateFnsLocale = zhCN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dateFnsLocale = enUS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return dateFnsLocale;
|
||||||
|
}
|
||||||
|
}
|
@ -20,3 +20,4 @@ export * from './file-utils';
|
|||||||
export * from './moment-date-formats.model';
|
export * from './moment-date-formats.model';
|
||||||
export * from './moment-date-adapter';
|
export * from './moment-date-adapter';
|
||||||
export * from './string-utils';
|
export * from './string-utils';
|
||||||
|
export * from './date-fns-utils';
|
||||||
|
@ -45,7 +45,7 @@ export class SearchSliderPage {
|
|||||||
async setValue(value: number): Promise<void> {
|
async setValue(value: number): Promise<void> {
|
||||||
const elem = this.filter.$(this.slider).$('.mat-slider-wrapper');
|
const elem = this.filter.$(this.slider).$('.mat-slider-wrapper');
|
||||||
await browser.actions().mouseMove(elem, { x: 0, y: 0 }).perform();
|
await browser.actions().mouseMove(elem, { x: 0, y: 0 }).perform();
|
||||||
await browser.actions().mouseDown().mouseMove({x: value * 10, y: 0}).mouseUp().perform();
|
await browser.actions().mouseDown().mouseMove({x: value * 20, y: 0}).mouseUp().perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkSliderIsDisplayed(): Promise<void> {
|
async checkSliderIsDisplayed(): Promise<void> {
|
||||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -18,6 +18,7 @@
|
|||||||
"@angular/core": "14.1.3",
|
"@angular/core": "14.1.3",
|
||||||
"@angular/forms": "14.1.3",
|
"@angular/forms": "14.1.3",
|
||||||
"@angular/material": "14.1.2",
|
"@angular/material": "14.1.2",
|
||||||
|
"@angular/material-date-fns-adapter": "^14.1.2",
|
||||||
"@angular/material-moment-adapter": "14.1.2",
|
"@angular/material-moment-adapter": "14.1.2",
|
||||||
"@angular/platform-browser": "14.1.3",
|
"@angular/platform-browser": "14.1.3",
|
||||||
"@angular/platform-browser-dynamic": "14.1.3",
|
"@angular/platform-browser-dynamic": "14.1.3",
|
||||||
@ -1216,6 +1217,19 @@
|
|||||||
"rxjs": "^6.5.3 || ^7.4.0"
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/material-date-fns-adapter": {
|
||||||
|
"version": "14.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/material-date-fns-adapter/-/material-date-fns-adapter-14.1.2.tgz",
|
||||||
|
"integrity": "sha512-98XaKVCybB/6hveqBiVQ88XWklxW27U917Uwq6sIgf6k62ZvrhPMOyz/arTCuR/OYRgJyWV2ykUxdqvGzr28+Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/core": "^14.0.0 || ^15.0.0",
|
||||||
|
"@angular/material": "14.1.2",
|
||||||
|
"date-fns": "^2.23.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular/material-moment-adapter": {
|
"node_modules/@angular/material-moment-adapter": {
|
||||||
"version": "14.1.2",
|
"version": "14.1.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -62,6 +62,7 @@
|
|||||||
"@angular/core": "14.1.3",
|
"@angular/core": "14.1.3",
|
||||||
"@angular/forms": "14.1.3",
|
"@angular/forms": "14.1.3",
|
||||||
"@angular/material": "14.1.2",
|
"@angular/material": "14.1.2",
|
||||||
|
"@angular/material-date-fns-adapter": "^14.1.2",
|
||||||
"@angular/material-moment-adapter": "14.1.2",
|
"@angular/material-moment-adapter": "14.1.2",
|
||||||
"@angular/platform-browser": "14.1.3",
|
"@angular/platform-browser": "14.1.3",
|
||||||
"@angular/platform-browser-dynamic": "14.1.3",
|
"@angular/platform-browser-dynamic": "14.1.3",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user