mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
2.4.0 (#3522)
* [ADF-2914] support for number range patterns (#3282) * support for number range patterns * fix memory leak for tag actions * [ADF-2824] Reviewed docs for core and content services (#3290) * [ADF-2824] Reviewed card view docs * [ADF-2824] Reviewed doc for core and content services * [ADF-2824] Added class name exception to prop script * [ADF-2843] added tooltip for create new folder and edit folder icons … (#3286) * [ADF-2843] added tooltip for create new folder and edit folder icons from Content Service * [ADF-2843] added translation for tooltip * Update content-metadata.component.md * [ADF_NOISSUE] Fix styling and sidenav layout fixes (#3291) * Fix styling and sidenav layout fixes * Revert packagr conflicting mods * [ADF-2905] Added JSDocs for process services (#3292) * [ADF-2905] Added JSDocs for process services * [ADF-2905] Added missing return value in tasklist service * [ADF-2916] number range form validator added (#3279) * number range form validator added * tests added * [ADF-2626] added preserved state functionality for sidenav component (#3278) * [ADF-2626] added preserved state functionality for sidenav component * [ADF-2626] changed logic for preserving the sidenav component state * [ADF-2626] added missing curly brace } * [ADF-2626] small changes on logic based on pr comments * [ADF-2843] added tooltip for create folder and edit folder icons from Content Services * Revert "[ADF-2843] added tooltip for create folder and edit folder icons from Content Services" This reverts commitd5a7abb65b. * [ADF-2626] changed casting * [ADF-2626] updated documentation with event * [ADF-2626] removed app-config pipe because it was not being used * [ADF-2328] filtering support for facets and categories (#3293) * filtering support for facets and categories * fix tests * update variable names * [ADF-2973] Angular pipe to get application configuration settings (#3301) * app config pipe * update docs * [ADF-2849] Search Date Range - Set the format of the date from config (#3288) * format date chosen from the datePicker's calendar * format date on focusout event * fix tests & some code refactoring * more validation messages * unit tests * fix typecast error * move "dateFormat" to be part of the "date range" widget settings block * fix error on Moment "...Type 'moment.Moment' is not assignable to type 'moment.Moment'. Two different types with this name exist, but they are unrelated. Property 'isLocal' is missing in type 'Moment'..." * moment - use old version * change script - use recent version of moment * Added the working-with-nodes-api-service tutorial. (#3295) * [ADF-2826] added a check for duplicate permission add (#3265) * [ADF-2556] first step to create add people or group to permissions * [ADF-2556] creating a dialog with user results * [ADF-2556] integrated service for add and remove permission from node * [ADF-2556] fixed behaviour and style for add user group * [ADF-2556] added some refactoring for dialog service * [ADF-2556] refactoring the dependencies of the components * [ADF-2556] added some fix and a new key for dialog * [ADF-2556] start adding test for node permission service * [ADF-2556] added test for add permission panel component * [ADf-2556] adding tests for new add permission component * [ADF-2556] fixed tests and added documentation * [ADF-2556] fixed documentation for add-node components * [ADF-2556] added peer review changes * [ADF-2826] added a check for duplicate permission add * [ADF-2826] removed fdescribe * [ADF-2826] applied peer review changes * [ADF-2930] general purpose "Empty Page" component (#3296) * empty content component * fix selector name * style fixes * cleanup code * docs for empty content * update docs * update docs * use typography for icon * layout fixes for Login (ported from ACA) * fix aot crash * fix tslint error and add and block if tslint give error in the pipeline * [ADF-2892] Sidenav now prevents closing itself when ESC key is pressed (#3302) * [ADF-2892] Sidenav now closes when clicking outside of it * [ADF-2892] Sidevan now prevents closing itself when ESC key is pressed * Fix for a technical bug, which prevents the proper usage of the expanded output event. (#3304) * [ADF-NO-PRIVATE] renoved private property which breaks AOT (#3305) * [ADF-2975] New input for File Upload Button component (#3299) * new nodeType input * unit tests * content type fix * remove package-lock.json files * [ADF-2699] added localisation for time ago pipe (#3298) * [ADF-2699] added localisation to the time-ago pipe * [ADF-2699] added lang to time ago pipe * [ADF-2699] added localisation for time ago pipe * [ADF-2699] removed fdescribe * [ADF-2699] removed comments * [ADF-2699] removed useless default values * fix css scope for search filter * fix aot error * fix ID show rancher script update * Addition to the preparation of the environment. (#3306) * Added the working-with-nodes-api-service tutorial. * Added the 'Alfresco Example Content Application' paragraph to the 'Preparing the development environment' tutorial. * [ADF-2843] added tooltips for all the buttons in toolbar (#3297) * [ADF-2843] added tooltip for create new folder and edit folder icons from Content Service * [ADF-2843] added translation for tooltip * [ADF-2843] added tooltips for all the buttons in toolbar * [ADF-2843] discard package-lock * [ADF-2832] discard changes to package-lock * [ADF-2843] discarded changes from package-lock * [ADF-2843] added tooltip for list view button * [ADF-2912] added group everyone as constant result for add permissions (#3266) * [ADF-2556] first step to create add people or group to permissions * [ADF-2556] creating a dialog with user results * [ADF-2556] integrated service for add and remove permission from node * [ADF-2556] fixed behaviour and style for add user group * [ADF-2556] added some refactoring for dialog service * [ADF-2556] refactoring the dependencies of the components * [ADF-2556] added some fix and a new key for dialog * [ADF-2556] start adding test for node permission service * [ADF-2556] added test for add permission panel component * [ADf-2556] adding tests for new add permission component * [ADF-2556] fixed tests and added documentation * [ADF-2556] fixed documentation for add-node components * [ADF-2556] added peer review changes * [ADF-2912] added group everyone as constant result for add permissions * [ADF-2912] readded jsdoc * fix search filter styles (#3313) * [ADF-2978] added a check when locally set permission is undefined (#3307) * [ADF-2969] Moved doc tools to new tools folder (#3314) * [ADF-2969] Moved doc tools to new tools folder * [ADF-2969] Added files missing from schematic * [ADF-2969] Added missing files to schematic * [ADF-2813] set default sorting (#3311) * [ADF-2891] decoupled search service love (#3312) * Added the tutorial 'Creating your JavaScript application using alfresco-js-api'. (#3310) * Added the working-with-nodes-api-service tutorial. * Added the 'Alfresco Example Content Application' paragraph to the 'Preparing the development environment' tutorial. * Added the tutorial 'Creating your JavaScript application using alfresco-js-api'. * Added the image of the tutorial. * Typo in 'creating-javascript-app-using-alfresco-js-api.md' Typo in 'Prerequisites'. * Typos in 'creating-javascript-app-using-alfresco-js-api.md' Two small typos. * Wrong sentence in 'creating-javascript-app-using-alfresco-js-api.md' ...into Angular applications, but it is "framework agnostic". * Typo on 'creating-javascript-app-using-alfresco-js-api.md'. ...and Angular... * [ADF-2888] UX doesn't respect the Sidenav specifiactions (#3303) * [ADF-2771] Sidebar action menu component - UX review * Add a input property to set the width of sidebar-action-menu. * [ADF-2888] UX doesn't respect the Sidenav specifiactions * Add a input property to set the width of sidebar-action-menu. * remove search configuration enforcement (#3315) * [ADF-2986] try to pick the start value from local storage before the standard default (#3318) * [ADF-2912] group everyone is always visible even for no search result (#3316) * [ADF-2826] fixed wrong parsing for error message (#3317) * [ADF-2739] Improved breadcrumb logic (#3287) * [ADF-2739] Long names in breadcrumb fixed * [ADF-2739] Updated styles * [ADF-2739] Fixing @mixin for breadcrumb * [ADF-2739] Waiting for changes in demo-shell * [ADF-2739] Fixed @mixin * [ADF-2739] Fixed issue related to breadcrumb position * [ADF-2739] Improved ngOnChanges call for breadcrumb * [ADF-2739] Fixed issues with lint * [ADF-2739] Removed comment in dropdown breadcrumb component file * [ADF-2739] Changed recalculateNodes method from public to protected * pipeline-update (#3309) * deploy PR script * [ADF-2967] fixed create new task navigation (#3322) * [ADF-2967] fixed create new task navigation * [ADF-2967] removed extra variables * [ADF-2967] removed unused methods * [ADF-2961] support for protocol substition in app config files (#3324) * support for protocol substition in app config files * Update app-config.service.spec.ts * Update app-config.service.spec.ts * [ADF-2925] Required and invalid validators added (#3277) * test added * tests clean * test added * tslint clean * test name changed * update tests * Added the tutorial 'Working with the Nodes API Service'. (#3319) * [ADF-2979] Updated props script to avoid copying missing JSDocs (#3327) * [ADF-2979] Updated tools to ignore blank JSDocs for inputs/outputs * [ADF-2979] Bug fixes for handling missing tables, etc * [ADF-2989] updated documentation with multiple node example (#3328) * [ADF-2503] conditional visibility for content actions (#3325) * conditional visibility for content actions * fix typo * workaround for "target: all" * [ADF-2821] move module sidebar action menu (#3321) * move module sidebar action menu * fix core import test * remove sidebar module * skip error * skiperror old versions * [ADF-2995] Permissions - Consumer should be able to upload a new version for his file on a private site (#3326) * check node permission instead of parent * tests * Fix sidenav action icons (#3331) * Only the username form control should be emitted (#3333) * about component (#3337) * [ADF-2563] Improve versioning functionality (#3335) * change input with textarea * update file version use now the update content API * provide way to test read only mode version list * fix test * test fix * [ADF-2997] The meaning of the range fields is not clear (#3338) * [ADF-2997] restrict 'to' field not to allow dates in the future * [ADF-2997] more clear labels for start and end date fields the translation team will decide what will be the best values for the date fields * [ADF-3000] Update the documentation by adding the steps of date format custom configuration. (#3339) * [ADF-3028] i18n support for title service (#3342) * i18n support for title service * cleanup tests * update tests * fixed constraint for viewer name width (#3343) * (demo shell) login css fixes (#3344) * [ADF-1997] override custom form widgets by default (#3346) * override custom form widgets by default * update tests * [ADF-2979] Updated doc tools to avoid erasing MD docs with blank JSDocs in services (#3347) * [ADF-2979] Update to ignore blank JSDocs for method signature * [ADF-2979] Finished adding blank JSDoc check for services * [ADF-2131] Search sorting (#3334) * sorting configuration * detect primary sorting and use with document list * search results sorting * docs update * unit tests and code updates * update code * update code * generic sorting picker, test updates * ability to switch off client side sorting * update docs for document list * [ADF-2884] Task List - Custom apps display all tasks (#3329) * [ADF-2884] Handled filterParam input change Changed component selector * [ADF-2884] Modified/Added tests * [ADF-2884] Deprecated old selectors * [ADF-2884] Added deprecated version * [ADF-2884] Improved filter finding condition * update project permissions change * [ADF-2984] Show date invalid message on search date range picker (#3323) * [ADF-2984] Show date invalid message on search date range picker * [ADF-2984] test that required format is displayed when date input is invalid * [ADF-2984] More space above buttons * dummy commit * restore deleted breadcrumb code * [ADF-2753] New error component (#3336) * [ADF-1938] Overflowing text in reports section fidex * [ADF-1938] Long names in report section now fit * [ADF-1938] Reverted changes in container widget * [ADF-2753] New error component created * [ADF-2753] Unit test for Error Content Component * Deleting unused files * Deleting unused files * Deleting unused files * [ADF-2753] Documentation added * [ADF-2753] Fixed minor bugs * [ADF-2753] Authentication not needed to view error * add error handler * tslint fix * router app component * remove unused import * fix import modules * limit to 404 * destroy fixture after any test * misspelling error * breadcrumb bug fixes (#3348) * [ADF-2726] fixed save content for external repository (#3341) * [ADF-2726] start fixing the show of files loaded from CS * [ADF-2726] start fixing the show of files loaded from CS * [ADF-2726] fixed save content for external repository| * [ADF-2726] fixed save content for external repository| * [ADF-2726] reeanabled and fixed the tests * [ADF-2726] reeanabled and fixed the tests * [ADF-2726] added tests for attach file widget and activiti alfresco service * [ADF-2726] added tests for attach file widget and activiti alfresco service * [ADF-2726] fixed test * fix like test * fix typo in the resource key (#3352) * fix failing tests replace ajax with spy * remove fdescribe * [ADF-3040] Replaced doc template engine to fix whitespace issues (#3356) * replace jasmine ajax with spy in rating tests * fix breadcrumb align in object picker (#3353) * fix no content node display (#3351) * Broken links and missing images for two tutorials. (#3357) * Broken links and missing images for two tutorials. * Replaced .html with .md extensions Replaced .html with .md extensions. * Typo in Prerequi(si)tes Typo in Prerequi(si)tes. * [ADF-2988] fix logo change (#3362) * [ADF-3055] fix bug with page size for facet fields (#3359) * fix bug with page size for facet fields * move page size to a constant * move const inside the scope * test fixes * [ADF-2923] button type added (#3358) * button type added * remove unnecessary code * [ADF-2608] added a fix to handle selection on slow connection problem (#3350) * [ADF-2608] added a fix to handle selection on slow connection problem * [ADF-2608] fixed test for datatable * Added an import command to the 'Adding a new view' tutorial. (#3366) * remove redundant logic (#3361) * [ADF-2679] Reviewed latest tutorial content (#3371) * [ADF-2679] Initial review of new tutorial content * [ADF-2679] Reviewed the latest tutorials * [ADF-2679] Initial review of new tutorial content * [ADF-2679] Reviewed the latest tutorials * [ADF-2832] Demo-shell, Fix Task filter dropdown menu alignment (#3294) * [ADF-2832] changed height to auto for task filter dropdown header * [ADF-2832] change selector with classes * [ADF-2832] changed selector * [ADF-2832] removed unnecessary css * [ADF-2832] added class and changed selector * [ADF-2832] added adf-prefix * [ADF-2927] search filter enhancements (#3365) * search filter enhancements * reset button for facet queries * update code and tests * remove unused type * restore code missing after rebase * [ADF-2764] Updated doc files with latest script features (#3368) * [ADF-2764] Updated doc files with latest script features * [ADF-2764] Rebuilt full index instead of just content services index * [ADF-2985] Demo shell - Provide an easy way to test the security issue (#3354) * Provide an easy way to test the security issue * use logService * first part random test fix (#3376) fixing random test executions first part * fix form radio test * [ADF-3065] TaskList component - Deprecate the processDefinitionKey (#3369) * Deprecate the processDefinitionKey property. remove unused tasklist code. It was needed with the old pagination component * Remove nosense test * Use adf version * [ADF-2999] readded form validation icon (#3367) * [ADF-2999] readded form validation icon * [ADF-2999] fixed broken test * Added the tutorial 'Basic theming'. (#3378) * [ADF-2854] Fix demoshell task/process filter and routing (#3360) * Fix demoshell task/process filter and routing * Use the correct js api model * Fix unit test * search filter now remembers original user query (#3384) * [ADF-2319] added a check for delete checklist button (#3382) * [ADF-2238] removed created by property (#3380) * [ADF-2238] removed created by property * [ADF-2238] added a condition to avoid checklist delete for completed task * Revert "[ADF-2238] added a condition to avoid checklist delete for completed task" This reverts commitfc2227ea04. * bold font for checked boxes (#3381) * [ADF-2774] fixed message bus, background and language dropdowns that were hidden by the toolbar (#3379) * [ADF-2764] Applied new doc tool features to core library (#3383) * [ADF-2824] Added and reviewed some content services docs (#3385) * revert changes (#3387) * Added the 'Content metadata component' tutorial. (#3386) * update package settings and lock file (#3389) * minor layout fix for search facet buttons * [ADF-2857] fixed message for date and date time widget (#3370) * [ADF-2764] Updated process services docs with latest features (#3390) * [ADF-2977] made language change persistenr (#3373) * [ADF-3041] TaskList Component - Empty State issue. (#3345) * [DW-635] Empty State Component * [DW-635] Empty State Component * [DW-635] Empty state Issue * [DW-635] Use empty state component and custom empty directive * [ADF-3041] Documentation for TaskList Component - Empty State issue. * [ADF-3088] move sorting picker to a separate module (#3396) * move sorting picker to a separate module * update translation mock * [ADF-3087] Clarified section about adding/replacing keys in i18n guide (#3392) * [ADF-3087] Clarified section about adding/replacing keys * [ADF-3087] Corrected information in i18n and translation service docs * [ADF-3068] ADF Sidenav/Toolbar - Ux background colour review (#3401) * [ADF-2824] Reviewed docs for new components in 2.4 (#3400) * [ADF-3042] Use the custom date adapter from adf-core on Search Date Range widget (#3394) -fix localization -fix tests -show parse error * [ADF-3053] breadcrumb fixes (#3406) * translate breadcrumb root in demo shell * optional "max items" feature and style fixes * should not be restricted by default * style updates * toolbar and breadcrumb layout fixes * breadcrumb demo and testing page * full toolbar scenario * fix translation issue with the dropdown and custom root * a11y fixes * fix issue with duplicate id, remove unused attribute * [ADF-2627] Icons-only mode for Info Drawer (#3398) * tab with icon * docs * test * add option without login docker publish * fix publish script * move docker push outside login * fix random test failing part 2 (#3395) * fix random failing test core search/comment/auth/user * fix node delete directive * fix lint issues * node restore fix * fix comment test * remove fdescribe * fix tests and tslint * fix upload test * unsubscribe success event task test * copy comment object during test * use the data pipe performance improvement and standard usage * uncomment random test * fix comment date random failing test * disposable unsubscribe * fix start process * remove fdescribe * change start process test and remove commented code * fix error event check double click * clone object form test * refactor date time test * fix service mock * fix test dropdown and context * git hook lint * fix language test * unsubscribe documentlist event test * fix disposable error * fix console log service error document list * unusbscribe search test * clear input field * remove wrong test * [ADF-2754] People Widget - Refactoring style and logic (#3349) * observable logic added * tests added & bug fixed * onkeyup removed * isValidUser method removed * tslint fix * test added * comments fixed * check if input value is string * remove console log err from document list test * [ADF-3082] Task Filter - Custom Task filters don't work (#3402) * [ADF-3082] Custom Task filters don't work. * Added an sorting input to the datatable. * Updated documentation for the recent changes. * Added testcases for the recent changes. * [ADF-3082] Custom Task filters don't work * Added a sorting input to the datatable * Added testcases to the recent changes. * Updated doc for the recent changes. * * Refactored task/process list dataSort. * * Refactored process/task list datasort method * remove console log error from version list and content node selector test stubbing necessary method * [ADF-3041] - Task List Empty State Issue (#3408) * fix demo shell gallery view switch (#3413) * [ADF-2541] reset datatable selection when rows are changed from code (#3410) * reset selection when rows are replaced from code * code fixes * unit test updates * visualise selection count for testing purposes * make row selection api public * remove question mark from the event name * remove pdfviewer thumbnail console log test error * fix conflict link creation id (#3412) * [ADF-2753] Error Component throwing default error fixed (#3364) * [ADF-1938] Overflowing text in reports section fidex * [ADF-1938] Long names in report section now fit * [ADF-1938] Reverted changes in container widget * [ADF-2753] New error component created * [ADF-2753] Unit test for Error Content Component * Deleting unused files * Deleting unused files * Deleting unused files * [ADF-2753] Documentation added * [ADF-2753] Error Component throwing default error fixed * [ADF-2753] White space removed * [ADF-2753] Removed unnecessary files and updated trnaslation file * [ADF-2753] Changed link for button in error component * [ADF-2753] Updated doc file * [ADF-2753] Removed fdescribe * [ADF-2753] Improved code coverage * fix error AppTitle service test log * fix gallery view demo shell route * [ADF-3095] ability to intercept, pause and resume upload process (#3416) * prevent and resume upload process * upload fixes and confirmation dialog demo * ability to toggle the upload confirmation demo * fix tests * unit tests * docs update * remove deprecation * fix test name * [ADF-2698] Date and time Widget - UTC vs local time issue (#3393) * [ADF-2698] Date and time Widget - UTC vs local time issue * [ADF-2698] add unit test to check that the format for min and max values is correct * [ADF-2698] test behavior * [ADF-3102] added lazy loading for tab content to fix animations (#3418) * [ADF-2764] Fixed union type display glitches in property tables (#3419) * [ADF-2764] Test commit to fix handling of union types in Github docs * [ADF-2764] Fixed union type error in component docs * [ADF-2593] z-index fix for search bar (#3409) * [ADF-2593] * [ADF-2593] Removed unnecessary style attributes * [ADF-3111] pagination is not showed when automation test are running (#3420) * [ADF-3108] Search - facetQueries panel renders when not defined in configuration (#3417) * show facetQueries based on configuration definition * tests * FacetQueries is defined * [ADF-3039] Task List - Enanchement (#3404) * * Created DataColumnSchemaAssembler component to get column schema from html and app.config.json * Removed column related method from tasklist. * * Removed data property from the tasklist component * Using rows input property instead of data input property of the datatable * * Renamed DataColumnSchemaAssembler to DataTableSchema * Refactored DataTableSchema component * * Changed schem property into an input schemaColumns property in dataTable component * * Added selectFirstRow input property to select a first row of datatable * Removed unnecessary method from tasklist component * * Added test case for the recent changes * Added mock object for the tasklist spec * * Added testcases for recent changes in the datatable component * * Updated datatable and tasklist document for the recent changes * * Refactored process-service and task list component * Updated datatable document. * [ADF-3039] Task List - Enanchement * Changed schemaColumn name to columns * Updated datatable documentation. * data input Annotated with @deprecated in the tasklist component * * Added an sorting input to the datatable. * Updated datatable and tasklist documentation * Added method to get current sorting order. * * After rebasing * * Revert sorting changes * * After rebase * * fixed conflicts * * Fixed failing testcase after rebased. * update toolbar docs (#3423) * Add custom data holder for CardViewItems (#3422) * [ADF-3115] a11y fixes (#3424) * a11y fixes for search input * a11y fixes for pagination * a11y fixes for content actions * [ADF-3118] translation support for notification service (snackbars) (#3427) * translation support for snackbar * unit tests * [ADF-2438] fixed thumbnails height (#3407) * [ADF-2438] calculate thumbnail height * [ADF-2438] send height to parent element * [ADF-2438] add width to css * [ADF-2438] moved height and width logic to parent component * [ADF-2438] added height and width logic to parent component * [ADF-2438] fixed failing test * demo translation for search radio * [ADF-2710] removed default shared link creation at the opening (#3430) * [ADF-2999] added a way to test the validation icon on demo shell (#3431) * [ADF-2754] People component tests fixed and style improvement (#3414) * tests fixed and style * show people test fixed * error message test fixed * [ADF-3131] fix selection order for DT/DL (#3433) * fix selection order for DT/DL * remove fit * [ADF-3066] ProcessList Component - Empty State issue. (#3434) * [ADF-3093] Added style checking tool for en.json translation file (#3428) * [ADF-3093] Started li18nt VS Code extension * [ADF-3093] Started work on UI style lint tool for VSCode * [ADF-3093] Added UI style rules up to sg0006 * [ADF-3093] Added remaining style rules * [ADF-3093] Added docs and command line tool * [ADF-3093] Removed Microsoft notices and updated licences to Apache-2.0 * [ADF-3133] Fixed inconsistency in doc example (#3436) * [ADF-2753] Fixed routing and second button showing up (#3421) * [ADF-1938] Overflowing text in reports section fidex * [ADF-1938] Long names in report section now fit * [ADF-1938] Reverted changes in container widget * [ADF-2753] New error component created * [ADF-2753] Unit test for Error Content Component * Deleting unused files * Deleting unused files * Deleting unused files * [ADF-2753] Documentation added * [ADF-2753] Removed unnecessary files and updated trnaslation file * [] * [ADF-2753] Fixed routing and second button showing up * [ADF-2753] Fixed typo * [ADF-2753] Fixed view loading before variables * [ADF-2753] Missing whitespace * [ADF-2753] Added test for route params * [ADF-2753] Changed getData function name to getTranslations * [ADF-fix date widget test] Fixing a randomly failing test (#3439) * group uploaded files into single batch, IE fixes (#3438) * [ADF-3067] App-list Empty State. (#3432) * extend demo shell upload example to button * [ADF-2764] Added new type linker features and applied them to core docs (#3442) * [ADF-2764] Added basic support for composite and external types * [ADF-2764] Added new type linker features and applied to core docs * notification service demo and testing area (#3443) * notification service demo and testing area * fix typo * fix viewer title style (#3445) * [ADF-3132] TaskList empty state doesn't respect the specification (#3440) * [ADF-3134] fix increasing search calls issue (#3444) * [ADF-2764] Applied new type linker features to content services docs (#3446) * [ADF-3062] dual api support for Favorites (#3447) * dual api support for Favorites * unit test * ADF-2974] New Buttons Menu component version (#3429) * [ADF-2974] Buttons injected from parent html component * [ADF-2974] New version of buttons menu component * [ADF-2974] Updated unit tests * [ADF-2974] Updated documentation * [ADF-2974] Removed unused variable * [ADF-2974] Fixed failing test at analytics report parameters * [ADF-2974] Removed fdescribe * [ADF-2974] Moved mock inside testing file for buttons menu component * extend breadcrumb testing page (demo shell) * [ADF-3142] Added missing info about 'remember me' in login docs (#3448) * [ADF-3120] fixed sorting for tasklist and process list (#3435) * [ADF-3120] fixed sorting for tasklist and process list * [ADF-3120] removed commented code * [ADF-3120] fixing another randomly failing test * [ADF-2764] Applied new type linker features to proc services docs (#3449) * (demo shell) fix memory leak with search results * [ADF-2795] SSO implicitflow (#3332) * Enable OAUTH2 * Create SSO services * SSO improvements * Rollback sso login change * Add SSO configuration from Setting component * Refactoring * Remove login ECM/BPM toggle and move use the userpreference instead of store * fix host setting unit test * Fix unit test missing instance * use the Js api oauth * add logout component and clean sso not used class * fix dependencies cicle * add translation settings * fix style setting page * clean * JS APi should receive the oauth config from the userPreference and not from the config file * change login if SSO is present * missing spaces * add sso test in login component * add logout directive new properties test * Improve host setting and remove library reference * fix login test * Remove unused code * Fix authentication unit test * fix authguard unit test * fix csrf check login component * fix unit test core and demo shell * remove * simple filtering for datatable (#3453) * [ADF-3150] Moved all doc tool config settings to doctool.config.json (#3455) * [ADF-3150] Moved undoc stoplist to doc config file * [ADF-3150] Moved config to doctools.config.json and removed obsolete scripts * fixes for memory leaks and multiple subscriptions (#3456) * fix content action memory leaks * memory leak fixes for demo shell * [no-issue] error image resolver mimetype should not be part of datatable (#3415) * move error image custom logic form datatable to documentlist * change travis * [fix-test-log] added optional function to data row interface * [no-issue] fixed datatable tests * [no-issue] fixing tests * [ADF-2859] conditional evaluation of disabled state for content actions (#3450) * react on [disabled] binding changes * [disabled] binding updates for context menu items * evaluating disabled state with a function * unit test * restore original description * remove irrelevant test * fix tests * fix test * remove not used events in demo shell host setting components * [ADF-3156] App config default ALL (BPM and ECM) provider (#3460) * Use as default provider ALL (BPM and ECM) * Default host OAUTH * removing wrong fdescribe (#3461) * [ADF-3123] removed default choice for the first radio button (#3462) * Fix host component (#3463) * [ADF-2824] Doc review and minor changes to review checker tool (#3466) * restore historical selection order (#3467) * restore historical selection order * fix test * [ADF-2859] fixes for the conditional visibility and disabled states (#3465) * fixes for the conditional visibility and disabled states * update docs * cleanup code * remove unused code * [LOC-61] i18n 2.4.0 (#3468) * i18n 2.4.0 * missing part * revert development config * [ADF-3144] Error component displayed when closing file viewer in process tab (#3469) * [ADF-3173] [Task List] - Empty list message is not translated (#3471) * Added a missing translation keys. * Fix dropdown panel in viewer toolbar because of z-index (#3472) * [ADF-3162] Setting component - Should be able to render the providers passed as input (#3464) * Be able to change the available providers values * Add deprecated tag * add default providers * Fix empty OAuth and selected providers Improve the documentation * Fix BPM guard to check SSO condition * Add the oauthConfig again * SSO or Basic otpion auth setting change * fix host settings sso/basic add login documentation * remove test string * fix auth guard test * dispose upload emitter test events * remove space * exclude failing test * Update ADF packages version 2.4.0-beta13 (#3475) * [ADF-2824] Reviewed search docs for 2.4 (#3477) * [ADF-2824] Moved search widget docs into separate pages * [ADF-2824] Separated search widget page and added search filter service docs * [ADF-2824] Updated new search docs with doc tools * Remove unused field (#3480) * Fix default SSO config not filled in (#3479) * [ADF-3175] renabling upload on single target folder row (#3473) * [ADF-3191] Not able to login only to BPM or ECM (#3478) * disable random test * host setting component reset JS-API * reload api after user prefrence change * remove replicate reset * missing semicolon * deprecate provider property * remove trailing space * check new value before to set * [ADF-3198] Updated index files + minor doc updates (#3482) * [ADF-3200] custom Extension filter - file with different extension is uploaded with drag and drop * [ADF-3181] Auth Guard - redirect by string url (#3474) * redirect by string url * auth guard tests fix * revise * [ADF-3196] fixed task selection and double click (#3484) * [ADF-3191] Fix userprefrence oauth2 (#3488) * remove user preference setting save * fix host setting test * remove userPreferences test * fix title service test * remove unused imports * restore input align * fix service import in test tag rating * [ADF-3210] Typo People Search (#3487) * remove vscode changes * [ADF-3196] [Task list / Process list] - The 'Task details' are not diplayed for the selected Task (#3483) * [ADF-3196] [Task list / Process list] - The 'Task details' are not displayed for the selected Task * * After rebase * [ADF-3176] Fixed permissionsissue when editing metadata (#3476) * [ADF-3217] reset previous search settings (#3490) * reset previous search settings * unit test * remove fit * [ADF-3196] fixed selection of first tasklist element (#3491) * [ADF-3196] [Task list / Process list] - The 'Task details' are not displayed for the selected Task * * After rebase * [ADF-3196] fixed selection for fist element * [ADF-3212] Upload version buttons wrapping fixed (#3492) * Update ADF packages version 2.4.0-beta14 (#3493) * demo shell host setting component login redirect after setting * remove old auth test not valid anymore * fix translations (#3497) * [ADF-3203] Fixed doc tools bugs from Linux run and added verbose option (#3496) * [ADF-3912] Setting Component - Return as default BASIC authType (#3495) * Return as default BASIC authType * remove the commented line * [ADF-3190] Fix rerendering issue with content-meta-data (#3494) * Fix rerendering issue with content-meta-data * Fix indentation * [ADF-3198] Index and tutorial content review (#3500) * [ADF-3198] Fixed glitches in doc index files and tools * [ADF-3198] Reviewed new tutorial content and fixed index glitches * [ADF-3198] Fixed host settings component brief description * [ADF-3223] Restore message directive files fix (#3499) * fix restore notification * improve path specification property * fix lint and document issues * fix test * remove unused import * [ADF-3226] remove Double behaviour setting allow download and comment (#3502) * fix restore notification * improve path specification property * fix lint and document issues * fix test * remove unused import * remove double behavior allowComments and allowDonload can be set only thorough input property * [ADF-3233] fixed demo shell filtering for files on drag&drop] (#3504) * [ADF-3232] fixing retrieving for ecm user via site service (#3506) * [ADF-3176] file content reload fixed (#3501) * file content reload fixed * refactoring & layout fix * resolve indentation problems * missing semicolon * [ADF-3224] move ticket store logic in js-api (#3505) * move ticket store logic in js-api * remove space * fix test * fix tests * [ADF-3212] Fixed permission issues when updating version (#3503) * [ADF-3213] Fixed permission issues when updating version * [ADF-3212] fixed test for PR * allowableOperations include viewer get node * update demo shell with metadata demo * undo ADF-3176 changes * fix login oauth cookie problem (#3508) * fix login don't check the cookie in oauth2 * fix test * check boolean storage * fix provider check * clean local storage before auth test * fix missing pdf viewer update when scale is the same (#3510) * [ADF-3239] Fixed deprecated property descriptions (#3509) * [ADF-3239] Changed deprecated properties to include full description * [ADF-3239] Updated deprecated properties of affected doc files * [ADF-3239] Fixed badly formatted doc comment in tasklist * [ADF-3229] people widget list reseted if empty input (#3507) * list reseted when input is empty * test added * [ADF-3242] removed filter for string in example log component (#3511) * [ADF-3242] removed filter for string in example log component * [ADF-3242] added try catch for circular dependency objects * [ADF-3242] added try catch and removed error message * invalidate session in host setting internal component instead to logout * remove unused import * use i18n key for "Permission" label * [ADF-3198] Final doc checks for release (#3513) * [ADF-3198] Doc link check for release * [ADF-3198] Updated main index doc page * remove invalidation session because is concern of the js-api * check 401 and redirect demo shell (#3516) * [ADF-3248] fixed readonly status for people widget (#3512) * [ADF-3248] fixed readonly status for people widget * [ADF-3248] fixed test people widget * [ADF-3248] added readonly test * [ADF-3251] Authentication Service - Check if used loggedin based on the provider (#3515) * check if loggedin based on the provider * Fix unit tests * remove 404 check in demo shell * [ADF-3262] Remove avoidable css style from task/process list component (#3518) * [ADF-3230] Changes to stop doc tools from updating files unnecessarily (#3519) * [ADF-3230] Added basic change detector to doc tools * [ADF-3230] Updates to type linker to fix before/after inconsistencies * [ADF-3230] Fixed comparison error caused by adjacent text blocks * [ADF-3230] Added basic change detector to doc tools * [ADF-3230] Updates to type linker to fix before/after inconsistencies * [ADF-3230] Fixed comparison error caused by adjacent text blocks * [ADF-3230] Modified props tool to remove spaces from union types * [ADF-3230] Made ToC tool before/after state consistent * [ADF-3265] Updated index script to remove brief desc links (#3520) * bump 2.4.0 version
This commit is contained in:
@@ -1,40 +1,5 @@
|
||||
# Alfresco Angular Components
|
||||
|
||||
<!-- markdown-toc start - Don't edit this section. npm run toc to generate it-->
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
- [Documentation](#documentation)
|
||||
- [Build from sources](#build-from-sources)
|
||||
- [NPM scripts](#npm-scripts)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
<!-- markdown-toc end -->
|
||||
|
||||
## Documentation
|
||||
|
||||
The [docs index](../docs/README.md) lists all available documentation for components and
|
||||
also includes a user guide that explains techniques in greater detail.
|
||||
|
||||
|
||||
## Build from sources
|
||||
|
||||
You can build component from sources with the following commands:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
> The `build` task rebuilds all the code, runs tslint, license checks
|
||||
> and other quality check tools before performing unit testing.
|
||||
|
||||
## NPM scripts
|
||||
|
||||
| Command | Description |
|
||||
| --- | --- |
|
||||
| npm run build | Build component |
|
||||
| npm run test | Run unit tests in the console |
|
||||
| npm run test-browser | Run unit tests in the browser
|
||||
| npm run coverage | Run unit tests and display code coverage report |
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# Review checker guide
|
||||
|
||||
The review checker tool queries the Github repo to look for recent
|
||||
commits to the component source files. The dates of these commits
|
||||
are compared against against a review date stored in the Markdown doc
|
||||
file for each component. The time and the number of commits since the
|
||||
last review are then combined into a "score" that gives an indication
|
||||
of how urgently the doc file needs a review.
|
||||
|
||||
## Review date metadata
|
||||
|
||||
The review date is kept in the YAML metadata section at the top of each
|
||||
Markdown file. The key is "Last reviewed" and the date is in the form
|
||||
YYYY-MM-DD.
|
||||
|
||||
## Commit message stoplist
|
||||
|
||||
The checker will ignore any commits that match regular expressions stored
|
||||
in the `commitStoplist.json` file in the `DocProcessor` folder. You could
|
||||
use this, for example, to filter out JIRA tasks that don't involve any
|
||||
changes in functionality (and therefore don't need documenting).
|
||||
|
||||
## Output format
|
||||
|
||||
The script sends comma-separated text to the command line. You can copy/paste
|
||||
this into a spreadsheet or redirect the output to a text file with a ".csv"
|
||||
suffix.
|
||||
@@ -1 +0,0 @@
|
||||
["ADF-1769"]
|
||||
@@ -1,177 +0,0 @@
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var program = require("commander");
|
||||
|
||||
var remark = require("remark");
|
||||
var parse = require("remark-parse");
|
||||
var stringify = require("remark-stringify");
|
||||
var frontMatter = require("remark-frontmatter");
|
||||
|
||||
|
||||
// "Aggregate" data collected over the whole file set.
|
||||
var aggData = {};
|
||||
|
||||
var toolsFolderName = "tools";
|
||||
var configFileName = "doctool.config.json";
|
||||
var defaultFolder = path.resolve("..", "docs");
|
||||
|
||||
|
||||
function initPhase(aggData) {
|
||||
toolList.forEach(toolName => {
|
||||
toolModules[toolName].initPhase(aggData);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function readPhase(filenames, aggData) {
|
||||
for (var i = 0; i < filenames.length; i++) {
|
||||
var pathname = filenames[i];//path.resolve(srcFolder, filenames[i]);
|
||||
|
||||
var src = fs.readFileSync(pathname);
|
||||
var tree = remark().use(frontMatter, ["yaml"]).parse(src);
|
||||
|
||||
toolList.forEach(toolName => {
|
||||
toolModules[toolName].readPhase(tree, pathname, aggData);
|
||||
});
|
||||
}
|
||||
|
||||
//console.log(JSON.stringify(aggData.mdFileData));
|
||||
}
|
||||
|
||||
|
||||
function aggPhase(aggData) {
|
||||
toolList.forEach(toolName => {
|
||||
toolModules[toolName].aggPhase(aggData);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function updatePhase(filenames, aggData) {
|
||||
for (var i = 0; i < filenames.length; i++) {
|
||||
var pathname = filenames[i]; // path.resolve(srcFolder, filenames[i]);
|
||||
|
||||
var src = fs.readFileSync(pathname);
|
||||
var tree = remark().use(frontMatter, ["yaml"]).parse(src)
|
||||
|
||||
var modified = false;
|
||||
|
||||
toolList.forEach(toolName => {
|
||||
modified |= toolModules[toolName].updatePhase(tree, pathname, aggData);
|
||||
});
|
||||
|
||||
if (modified)
|
||||
fs.writeFileSync(filenames[i], remark().use(frontMatter, {type: 'yaml', fence: '---'}).data("settings", {paddedTable: false, gfm: false}).stringify(tree));
|
||||
|
||||
//console.log(JSON.stringify(tree));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadToolModules() {
|
||||
var mods = {};
|
||||
var toolsFolderPath = path.resolve(__dirname, toolsFolderName);
|
||||
var modFiles = fs.readdirSync(toolsFolderPath);
|
||||
|
||||
for (var i = 0; i < modFiles.length; i++) {
|
||||
var modPath = path.resolve(toolsFolderPath, modFiles[i])
|
||||
|
||||
if (path.extname(modPath) === ".js") {
|
||||
var toolName = path.basename(modPath, ".js");
|
||||
mods[toolName] = require(modPath);
|
||||
}
|
||||
}
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
|
||||
function loadToolConfig(configFilePath) {
|
||||
var config = JSON.parse(fs.readFileSync(configFilePath));
|
||||
|
||||
return config.enabledTools;
|
||||
}
|
||||
|
||||
|
||||
function getAllDocFilePaths(docFolder, files) {
|
||||
var items = fs.readdirSync(docFolder);
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var itemPath = path.resolve(docFolder, items[i]);
|
||||
var itemInfo = fs.statSync(itemPath);
|
||||
|
||||
if (itemInfo.isFile()){
|
||||
files.push(itemPath);
|
||||
} else if (itemInfo.isDirectory()) {
|
||||
getAllDocFilePaths(itemPath, files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
program
|
||||
.usage("[options] <source>")
|
||||
.parse(process.argv);
|
||||
|
||||
if (program.args.length === 0) {
|
||||
console.log("Error: source argument required");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
var sourcePath = path.resolve(program.args[0]);
|
||||
var sourceInfo = fs.statSync(sourcePath);
|
||||
|
||||
/*
|
||||
var destPath;
|
||||
var destInfo;
|
||||
|
||||
if (program.args.length >= 2) {
|
||||
destPath = path.resolve(program.args[1]);
|
||||
destInfo = fs.statSync(sourcePath);
|
||||
} else {
|
||||
destPath = sourcePath;
|
||||
destInfo = sourceInfo;
|
||||
}
|
||||
|
||||
if (sourceInfo.isDirectory() && !destInfo.isDirectory()) {
|
||||
console.log("Error: The <dest> argument must be a directory");
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
var toolModules = loadToolModules();
|
||||
var toolList = loadToolConfig(path.resolve(__dirname, configFileName));
|
||||
|
||||
|
||||
var files = [];
|
||||
|
||||
if (sourceInfo.isDirectory()) {
|
||||
getAllDocFilePaths(sourcePath, files);
|
||||
} else if (sourceInfo.isFile()) {
|
||||
files = [ sourcePath ];
|
||||
//files = [ path.basename(sourcePath) ];
|
||||
//sourcePath = path.dirname(sourcePath);
|
||||
//destPath = path.dirname(destPath);
|
||||
}
|
||||
|
||||
files = files.filter(filename =>
|
||||
(path.extname(filename) === ".md") &&
|
||||
(filename !== "README.md")
|
||||
);
|
||||
|
||||
//files.forEach(element => console.log(element));
|
||||
|
||||
console.log("Initialising...");
|
||||
initPhase(aggData);
|
||||
|
||||
console.log("Analysing Markdown files...");
|
||||
readPhase(files, aggData);
|
||||
|
||||
console.log("Computing aggregate data...");
|
||||
aggPhase(aggData);
|
||||
|
||||
console.log("Updating Markdown files...");
|
||||
updatePhase(files, aggData);
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"enabledTools": [
|
||||
"index",
|
||||
"versionIndex",
|
||||
"tutorialIndex"
|
||||
]
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
module.exports = searchLibraryRecursive;
|
||||
|
||||
var angFilenameRegex = /([a-zA-Z0-9\-]+)\.((component)|(directive)|(model)|(pipe)|(service)|(widget))\.ts/;
|
||||
var searchFolderOmitRegex = /(config)|(mock)|(i18n)|(assets)|(styles)/;
|
||||
|
||||
// Search source folders for .ts files to discover all components, directives, etc.
|
||||
function searchLibraryRecursive(srcData, folderPath) {
|
||||
var items = fs.readdirSync(folderPath);
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var itemPath = path.resolve(folderPath, items[i]);
|
||||
var info = fs.statSync(itemPath);
|
||||
|
||||
if (info.isFile() && (items[i].match(angFilenameRegex))) {
|
||||
var nameNoSuffix = path.basename(items[i], '.ts');
|
||||
|
||||
var displayPath = itemPath.replace(/\\/g, '/');
|
||||
displayPath = displayPath.substr(displayPath.indexOf("lib") + 4);
|
||||
|
||||
// Type == "component", "directive", etc.
|
||||
var itemType = nameNoSuffix.split('.')[1];
|
||||
|
||||
srcData[nameNoSuffix] = { "path": displayPath, "type": itemType };
|
||||
} else if (info.isDirectory() && !items[i].match(searchFolderOmitRegex)) {
|
||||
searchLibraryRecursive(srcData, itemPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
module.exports = {
|
||||
"ngNameToDisplayName": ngNameToDisplayName,
|
||||
"dekebabifyName": dekebabifyName,
|
||||
"classTypes": ["component", "directive", "model", "pipe", "service", "widget"]
|
||||
}
|
||||
|
||||
|
||||
function ngNameToDisplayName(ngName) {
|
||||
var mainSections = ngName.split(".");
|
||||
mainSections[0] = dekebabifyName(mainSections[0]);
|
||||
return mainSections.join(" ");
|
||||
}
|
||||
|
||||
|
||||
function dekebabifyName(name) {
|
||||
var result = name.replace(/-/g, " ");
|
||||
result = result.substr(0, 1).toUpperCase() + result.substr(1);
|
||||
return result;
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var process = require("process");
|
||||
var graphql_request_1 = require("graphql-request");
|
||||
var remark = require("remark");
|
||||
var frontMatter = require("remark-frontmatter");
|
||||
var yaml = require("js-yaml");
|
||||
var moment = require("moment");
|
||||
var Rx_1 = require("rxjs/Rx");
|
||||
var libsearch = require("./libsearch");
|
||||
var stoplist_1 = require("./stoplist");
|
||||
var adf20StartDate = "2017-11-20";
|
||||
var commitWeight = 0.1;
|
||||
var scoreTimeBase = 60;
|
||||
var rootFolder = ".";
|
||||
var stoplistFilePath = path.resolve("config", "DocProcessor", "commitStoplist.json");
|
||||
var angFilePattern = /(component)|(directive)|(model)|(pipe)|(service)|(widget)/;
|
||||
var srcData = {};
|
||||
var stoplist = new stoplist_1.Stoplist(stoplistFilePath);
|
||||
var docsFolderPath = path.resolve("..", "docs");
|
||||
var libFolders = ["core", "content-services", "process-services", "insights"];
|
||||
libsearch(srcData, path.resolve(rootFolder));
|
||||
/*
|
||||
let keys = Object.keys(srcData);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
console.log(keys[i]);
|
||||
}
|
||||
*/
|
||||
var authToken = process.env.graphAuthToken;
|
||||
var client = new graphql_request_1.GraphQLClient('https://api.github.com/graphql', {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + authToken
|
||||
}
|
||||
});
|
||||
var query = "query commitHistory($path: String) {\n repository(name: \"alfresco-ng2-components\", owner: \"alfresco\") {\n ref(qualifiedName: \"development\") {\n target {\n ... on Commit {\n history(first: 15, path: $path) {\n nodes {\n pushedDate\n message\n }\n }\n }\n }\n }\n }\n}";
|
||||
var docFiles = getDocFilePaths(docsFolderPath);
|
||||
var docNames = Rx_1.Observable.from(docFiles);
|
||||
console.log("'Name','Review date','Commits since review','Score'");
|
||||
docNames.subscribe(function (x) {
|
||||
var key = path.basename(x, ".md");
|
||||
if (!srcData[key])
|
||||
return;
|
||||
var vars = {
|
||||
"path": "lib/" + srcData[key].path
|
||||
};
|
||||
client.request(query, vars).then(function (data) {
|
||||
var nodes = data["repository"].ref.target.history.nodes;
|
||||
var lastReviewDate = getDocReviewDate(x); //(key + ".md");
|
||||
var numUsefulCommits = extractCommitInfo(nodes, lastReviewDate, stoplist);
|
||||
var dateString = lastReviewDate.format("YYYY-MM-DD");
|
||||
var score = priorityScore(lastReviewDate, numUsefulCommits).toPrecision(3);
|
||||
console.log("'" + key + "','" + dateString + "','" + numUsefulCommits + "','" + score + "'");
|
||||
});
|
||||
});
|
||||
function priorityScore(reviewDate, numCommits) {
|
||||
var daysSinceReview = moment().diff(reviewDate, 'days');
|
||||
var commitScore = 2 + numCommits * commitWeight;
|
||||
return Math.pow(commitScore, daysSinceReview / scoreTimeBase);
|
||||
}
|
||||
function getDocReviewDate(docFileName) {
|
||||
var mdFilePath = path.resolve(docsFolderPath, docFileName);
|
||||
var mdText = fs.readFileSync(mdFilePath);
|
||||
var tree = remark().use(frontMatter, ["yaml"]).parse(mdText);
|
||||
var lastReviewDate = moment(adf20StartDate);
|
||||
if (tree.children[0].type == "yaml") {
|
||||
var metadata = yaml.load(tree.children[0].value);
|
||||
if (metadata["Last reviewed"])
|
||||
lastReviewDate = moment(metadata["Last reviewed"]);
|
||||
}
|
||||
return lastReviewDate;
|
||||
}
|
||||
function extractCommitInfo(commitNodes, cutOffDate, stoplist) {
|
||||
var numUsefulCommits = 0;
|
||||
commitNodes.forEach(function (element) {
|
||||
if (!stoplist.isRejected(element.message)) {
|
||||
var abbr = element.message.substr(0, 15);
|
||||
var commitDate = moment(element.pushedDate);
|
||||
if (commitDate.isAfter(cutOffDate)) {
|
||||
numUsefulCommits++;
|
||||
}
|
||||
}
|
||||
});
|
||||
return numUsefulCommits;
|
||||
}
|
||||
function getDocFilePaths(folderPath) {
|
||||
var result = [];
|
||||
libFolders.forEach(function (element) {
|
||||
var libPath = path.resolve(folderPath, element);
|
||||
var files = fs.readdirSync(libPath);
|
||||
files = files.filter(function (filename) {
|
||||
return (path.extname(filename) === ".md") &&
|
||||
(filename !== "README.md") &&
|
||||
(filename.match(angFilePattern));
|
||||
});
|
||||
files.forEach(function (element) {
|
||||
result.push(path.join(libPath, element));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import * as process from "process"
|
||||
|
||||
import { GraphQLClient } from "graphql-request";
|
||||
import * as remark from "remark";
|
||||
import * as frontMatter from "remark-frontmatter";
|
||||
import * as yaml from "js-yaml";
|
||||
import * as moment from "moment";
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
import * as libsearch from "./libsearch";
|
||||
import { Stoplist } from "./stoplist";
|
||||
import { last } from "rxjs/operator/last";
|
||||
|
||||
|
||||
const adf20StartDate = "2017-11-20";
|
||||
|
||||
const commitWeight = 0.1;
|
||||
const scoreTimeBase = 60;
|
||||
|
||||
const rootFolder = ".";
|
||||
const stoplistFilePath = path.resolve("config", "DocProcessor", "commitStoplist.json");
|
||||
|
||||
const angFilePattern = /(component)|(directive)|(model)|(pipe)|(service)|(widget)/;
|
||||
|
||||
let srcData = {};
|
||||
let stoplist = new Stoplist(stoplistFilePath);
|
||||
|
||||
let docsFolderPath = path.resolve("..", "docs");
|
||||
|
||||
let libFolders = ["core", "content-services", "process-services", "insights"];
|
||||
|
||||
libsearch(srcData, path.resolve(rootFolder));
|
||||
|
||||
/*
|
||||
let keys = Object.keys(srcData);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
console.log(keys[i]);
|
||||
}
|
||||
*/
|
||||
|
||||
const authToken = process.env.graphAuthToken;
|
||||
|
||||
const client = new GraphQLClient('https://api.github.com/graphql', {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + authToken
|
||||
}
|
||||
});
|
||||
|
||||
const query = `query commitHistory($path: String) {
|
||||
repository(name: "alfresco-ng2-components", owner: "alfresco") {
|
||||
ref(qualifiedName: "development") {
|
||||
target {
|
||||
... on Commit {
|
||||
history(first: 15, path: $path) {
|
||||
nodes {
|
||||
pushedDate
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
let docFiles = getDocFilePaths(docsFolderPath);
|
||||
|
||||
let docNames = Observable.from(docFiles);
|
||||
|
||||
console.log("'Name','Review date','Commits since review','Score'");
|
||||
|
||||
docNames.subscribe(x => {
|
||||
let key = path.basename(x, ".md");
|
||||
|
||||
if (!srcData[key])
|
||||
return;
|
||||
|
||||
let vars = {
|
||||
"path": "lib/" + srcData[key].path
|
||||
};
|
||||
|
||||
client.request(query, vars).then(data => {
|
||||
let nodes = data["repository"].ref.target.history.nodes;
|
||||
|
||||
let lastReviewDate = getDocReviewDate(x);//(key + ".md");
|
||||
|
||||
let numUsefulCommits = extractCommitInfo(nodes, lastReviewDate, stoplist);
|
||||
let dateString = lastReviewDate.format("YYYY-MM-DD");
|
||||
let score = priorityScore(lastReviewDate, numUsefulCommits).toPrecision(3);
|
||||
|
||||
console.log(`'${key}','${dateString}','${numUsefulCommits}','${score}'`);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function priorityScore(reviewDate, numCommits) {
|
||||
let daysSinceReview = moment().diff(reviewDate, 'days');
|
||||
let commitScore = 2 + numCommits * commitWeight;
|
||||
return Math.pow(commitScore, daysSinceReview / scoreTimeBase);
|
||||
}
|
||||
|
||||
|
||||
function getDocReviewDate(docFileName) {
|
||||
let mdFilePath = path.resolve(docsFolderPath, docFileName);
|
||||
|
||||
let mdText = fs.readFileSync(mdFilePath);
|
||||
let tree = remark().use(frontMatter, ["yaml"]).parse(mdText);
|
||||
|
||||
let lastReviewDate = moment(adf20StartDate);
|
||||
|
||||
if (tree.children[0].type == "yaml") {
|
||||
let metadata = yaml.load(tree.children[0].value);
|
||||
|
||||
if (metadata["Last reviewed"])
|
||||
lastReviewDate = moment(metadata["Last reviewed"]);
|
||||
}
|
||||
|
||||
return lastReviewDate;
|
||||
}
|
||||
|
||||
|
||||
function extractCommitInfo(commitNodes, cutOffDate, stoplist) {
|
||||
let numUsefulCommits = 0;
|
||||
|
||||
commitNodes.forEach(element => {
|
||||
if (!stoplist.isRejected(element.message)) {
|
||||
let abbr = element.message.substr(0, 15);
|
||||
|
||||
let commitDate = moment(element.pushedDate);
|
||||
|
||||
if (commitDate.isAfter(cutOffDate)) {
|
||||
numUsefulCommits++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return numUsefulCommits;
|
||||
}
|
||||
|
||||
|
||||
function getDocFilePaths(folderPath) {
|
||||
let result = [];
|
||||
|
||||
libFolders.forEach(element => {
|
||||
let libPath = path.resolve(folderPath, element);
|
||||
let files = fs.readdirSync(libPath);
|
||||
|
||||
files = files.filter(filename =>
|
||||
(path.extname(filename) === ".md") &&
|
||||
(filename !== "README.md") &&
|
||||
(filename.match(angFilePattern))
|
||||
);
|
||||
|
||||
files.forEach(element => {
|
||||
result.push(path.join(libPath, element));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var fs = require("fs");
|
||||
/* "Stoplist" of regular expressions to match against strings. */
|
||||
var Stoplist = /** @class */ (function () {
|
||||
function Stoplist(slFilePath) {
|
||||
var listExpressions = JSON.parse(fs.readFileSync(slFilePath, 'utf8'));
|
||||
this.regexes = [];
|
||||
if (listExpressions) {
|
||||
for (var i = 0; i < listExpressions.length; i++) {
|
||||
this.regexes.push(new RegExp(listExpressions[i]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.regexes = [];
|
||||
}
|
||||
}
|
||||
// Check if an item is covered by the stoplist and reject it if so.
|
||||
Stoplist.prototype.isRejected = function (itemName) {
|
||||
for (var i = 0; i < this.regexes.length; i++) {
|
||||
if (this.regexes[i].test(itemName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return Stoplist;
|
||||
}());
|
||||
exports.Stoplist = Stoplist;
|
||||
@@ -1,31 +0,0 @@
|
||||
import * as fs from "fs";
|
||||
|
||||
/* "Stoplist" of regular expressions to match against strings. */
|
||||
export class Stoplist {
|
||||
regexes: RegExp[];
|
||||
|
||||
constructor(slFilePath: string) {
|
||||
let listExpressions = JSON.parse(fs.readFileSync(slFilePath, 'utf8'));
|
||||
this.regexes = [];
|
||||
|
||||
if (listExpressions) {
|
||||
for (var i = 0; i < listExpressions.length; i++) {
|
||||
this.regexes.push(new RegExp(listExpressions[i]));
|
||||
}
|
||||
} else {
|
||||
this.regexes = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Check if an item is covered by the stoplist and reject it if so.
|
||||
isRejected(itemName: string) {
|
||||
for (var i = 0; i < this.regexes.length; i++) {
|
||||
if (this.regexes[i].test(itemName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{% if hasInputs %}
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| -- | -- | -- | -- |
|
||||
{% each properties as prop %}
|
||||
{% if prop.isInput %}
|
||||
| {{{prop.name}}} | `{{{prop.type}}}` | {{{prop.defaultValue}}} | {{{prop.docText}}} |
|
||||
{% endif %}
|
||||
{% endeach %}
|
||||
{% endif %}
|
||||
{% if hasOutputs%}
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Type | Description |
|
||||
| -- | -- | -- |
|
||||
{% each properties as prop %}
|
||||
{% if prop.isOutput %}
|
||||
| {{prop.name}} | `{{{prop.type}}}` | {{{prop.docText}}} |
|
||||
{% endif %}
|
||||
{% endeach %}
|
||||
{% endif %}
|
||||
@@ -1,23 +0,0 @@
|
||||
{% if hasInputs %}
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| -- | -- | -- | -- |
|
||||
{% each properties as prop %}
|
||||
{% if prop.isInput %}
|
||||
| {{{prop.name}}} | `{{{prop.type}}}` | {{{prop.defaultValue}}} | {{{prop.docText}}} |
|
||||
{% endif %}
|
||||
{% endeach %}
|
||||
{% endif %}
|
||||
{% if hasOutputs%}
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Type | Description |
|
||||
| -- | -- | -- |
|
||||
{% each properties as prop %}
|
||||
{% if prop.isOutput %}
|
||||
| {{prop.name}} | `{{{prop.type}}}` | {{{prop.docText}}} |
|
||||
{% endif %}
|
||||
{% endeach %}
|
||||
{% endif %}
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
{% if hasMethods %}
|
||||
### Methods
|
||||
|
||||
{% each methods as meth %}- `{{meth.name}}{{{meth.signature}}{% if meth.returnsSomething %}: {{{meth.returnType}}}{% endif %}`<br/>
|
||||
{{meth.docText}}
|
||||
{% each meth.params as param %}
|
||||
- `{{{param.combined}}}` - {% if param.isOptional %}(Optional){% endif %}{{{param.docText}}}
|
||||
{% endeach %}
|
||||
{% if meth.returnsSomething %}
|
||||
- **Returns** `{{{meth.returnType}}}` - {{{meth.returnDocText}}}
|
||||
{% endif %}
|
||||
{% endeach %}
|
||||
{% endif %}
|
||||
@@ -1,5 +0,0 @@
|
||||
| Name | Level | Abstract |
|
||||
| -- | -- | -- |
|
||||
{% each tuts as tut %}
|
||||
| [**{{tut.title}}**]({{tut.link}}) | {{tut.level}} | {{tut.briefDesc}} |
|
||||
{% endeach %}
|
||||
@@ -1,401 +0,0 @@
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
|
||||
var remark = require("remark");
|
||||
var stringify = require("remark-stringify");
|
||||
var zone = require("mdast-zone");
|
||||
var yaml = require("js-yaml");
|
||||
|
||||
var unist = require("../unistHelpers");
|
||||
var ngHelpers = require("../ngHelpers");
|
||||
var searchLibraryRecursive = require("../libsearch");
|
||||
|
||||
module.exports = {
|
||||
"initPhase": initPhase,
|
||||
"readPhase": readPhase,
|
||||
"aggPhase": aggPhase,
|
||||
"updatePhase": updatePhase
|
||||
}
|
||||
|
||||
var angFilenameRegex = /([a-zA-Z0-9\-]+)\.((component)|(directive)|(model)|(pipe)|(service)|(widget))\.ts/;
|
||||
var searchFolderOmitRegex = /(config)|(mock)|(i18n)|(assets)|(styles)/;
|
||||
|
||||
var docsFolderPath = path.resolve("..", "docs");
|
||||
var undocStoplistFileName = path.resolve(docsFolderPath, "undocStoplist.json");
|
||||
var rootFolder = ".";
|
||||
var indexMdFilePath = path.resolve(docsFolderPath, "README.md");
|
||||
|
||||
var guideFolderName = "user-guide";
|
||||
var guideSummaryFileName = path.resolve(docsFolderPath, guideFolderName, "summary.json");
|
||||
|
||||
var maxBriefDescLength = 180;
|
||||
|
||||
var adfLibNames = ["core", "content-services", "insights", "process-services"];
|
||||
|
||||
var deprecatedIconURL = "docassets/images/DeprecatedIcon.png";
|
||||
var experimentalIconURL = "docassets/images/ExperimentalIcon.png";
|
||||
|
||||
|
||||
function initPhase(aggData) {
|
||||
aggData.stoplist = makeStoplist(undocStoplistFileName);
|
||||
aggData.srcData = {};
|
||||
aggData.mdFileDesc = [];
|
||||
aggData.mdFileStatus = [];
|
||||
aggData.mdFilePath = [];
|
||||
searchLibraryRecursive(aggData.srcData, path.resolve(rootFolder));
|
||||
|
||||
//console.log(JSON.stringify(aggData.srcData));
|
||||
}
|
||||
|
||||
|
||||
function readPhase(tree, pathname, aggData) {
|
||||
var itemName = path.basename(pathname, ".md");
|
||||
|
||||
// Look for the first paragraph in the file by skipping other items.
|
||||
// Should usually be at position 1 in the tree.
|
||||
var s;
|
||||
var briefDesc;
|
||||
|
||||
if (tree.children[0].type == "yaml")
|
||||
s = 1;
|
||||
else
|
||||
s = 0;
|
||||
|
||||
for (;
|
||||
(s < tree.children.length) && !unist.isParagraph(tree.children[s]);
|
||||
s++
|
||||
);
|
||||
|
||||
if (s < tree.children.length) {
|
||||
briefDesc = tree.children[s];
|
||||
}
|
||||
|
||||
aggData.mdFileDesc[itemName] = briefDesc;
|
||||
|
||||
if (tree.children[0].type == "yaml") {
|
||||
var metadata = yaml.load(tree.children[0].value);
|
||||
var status = metadata["Status"];
|
||||
|
||||
if (status) {
|
||||
var compName = path.basename(pathname, ".md");
|
||||
aggData.mdFileStatus[compName] = status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var linkPath = pathname.replace(/\\/g, '/');
|
||||
linkPath = linkPath.substr(linkPath.indexOf("docs") + 5);
|
||||
aggData.mdFilePath[itemName] = linkPath;
|
||||
}
|
||||
|
||||
function aggPhase(aggData) {
|
||||
var sections = prepareIndexSections(aggData);
|
||||
//console.log(JSON.stringify(sections));
|
||||
|
||||
var indexFileText = fs.readFileSync(indexMdFilePath, "utf8");
|
||||
var indexFileTree = remark().parse(indexFileText);
|
||||
|
||||
for (var l = 0; l < adfLibNames.length; l++) {
|
||||
|
||||
var libName = adfLibNames[l];
|
||||
var libSection = sections[libName];
|
||||
|
||||
var md = makeLibSectionMD(libSection, false);
|
||||
|
||||
zone(indexFileTree, libName, (startComment, oldSection, endComment) => {
|
||||
md.unshift(startComment);
|
||||
md.push(endComment);
|
||||
return md;
|
||||
});
|
||||
|
||||
md = makeLibSectionMD(libSection, true);
|
||||
|
||||
var subIndexFilePath = path.resolve(docsFolderPath, libName, "README.md");
|
||||
var subIndexText = fs.readFileSync(subIndexFilePath, "utf8");
|
||||
var subIndexTree = remark().parse(subIndexText);
|
||||
|
||||
zone(subIndexTree, libName, (startComment, oldSection, endComment) => {
|
||||
md.unshift(startComment);
|
||||
md.push(endComment);
|
||||
return md;
|
||||
});
|
||||
|
||||
subIndexText = remark().data("settings", {paddedTable: false}).stringify(subIndexTree);
|
||||
fs.writeFileSync(subIndexFilePath, subIndexText);
|
||||
}
|
||||
|
||||
var guideSection = buildGuideSection(guideSummaryFileName, false);
|
||||
|
||||
zone(indexFileTree, "guide", (startComment, oldSection, endComment) => {
|
||||
return [
|
||||
startComment, guideSection, endComment
|
||||
]
|
||||
});
|
||||
|
||||
fs.writeFileSync(path.resolve("..", "docs", "README.md"), remark().data("settings", {paddedTable: false}).stringify(indexFileTree));
|
||||
|
||||
guideSection = buildGuideSection(guideSummaryFileName, true);
|
||||
|
||||
subIndexFilePath = path.resolve(docsFolderPath, "user-guide", "README.md");
|
||||
subIndexText = fs.readFileSync(subIndexFilePath, "utf8");
|
||||
subIndexTree = remark().parse(subIndexText);
|
||||
|
||||
zone(subIndexTree, "guide", (startComment, oldSection, endComment) => {
|
||||
return [
|
||||
startComment, guideSection, endComment
|
||||
]
|
||||
});
|
||||
|
||||
subIndexText = remark().data("settings", {paddedTable: false}).stringify(subIndexTree);
|
||||
fs.writeFileSync(subIndexFilePath, subIndexText);
|
||||
|
||||
//fs.writeFileSync(indexMdFilePath, remark().stringify(indexFileTree));
|
||||
}
|
||||
|
||||
|
||||
function updatePhase(tree, pathname, aggData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Create a stoplist of regular expressions.
|
||||
function makeStoplist(slFilePath) {
|
||||
var listExpressions = JSON.parse(fs.readFileSync(slFilePath, 'utf8'));
|
||||
|
||||
var result = [];
|
||||
|
||||
for (var i = 0; i < listExpressions.length; i++) {
|
||||
result.push(new RegExp(listExpressions[i]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if an item is covered by the stoplist and reject it if so.
|
||||
function rejectItemViaStoplist(stoplist, itemName) {
|
||||
for (var i = 0; i < stoplist.length; i++) {
|
||||
if (stoplist[i].test(itemName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function prepareIndexSections(aggData) {
|
||||
var srcNames = Object.keys(aggData.srcData);
|
||||
var sections = initEmptySections();
|
||||
|
||||
for (var i = 0; i < srcNames.length; i++) {
|
||||
var itemName = srcNames[i];
|
||||
var srcData = aggData.srcData[itemName];
|
||||
var libName = srcData.path.substr(0, srcData.path.indexOf("/"));
|
||||
|
||||
var briefDesc = aggData.mdFileDesc[itemName];
|
||||
|
||||
var displayName = ngHelpers.ngNameToDisplayName(itemName);
|
||||
var pathname = aggData.mdFilePath[itemName];
|
||||
|
||||
var status = "";
|
||||
|
||||
if (aggData.mdFileStatus[itemName])
|
||||
status = aggData.mdFileStatus[itemName];
|
||||
|
||||
if (briefDesc) {
|
||||
sections[libName][srcData.type].documented.push({
|
||||
"displayName": displayName,
|
||||
"mdName": itemName + ".md",
|
||||
"mdPath": pathname,
|
||||
"srcPath": srcData.path,
|
||||
"briefDesc": briefDesc,
|
||||
"status": status
|
||||
});
|
||||
} else if (!rejectItemViaStoplist(aggData.stoplist, itemName)) {
|
||||
sections[libName][srcData.type].undocumented.push({
|
||||
"displayName": displayName,
|
||||
"mdName": itemName + ".md",
|
||||
"srcPath": srcData.path
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
|
||||
function initEmptySections() {
|
||||
var result = {};
|
||||
|
||||
for (var l = 0; l < adfLibNames.length; l++) {
|
||||
var lib = result[adfLibNames[l]] = {};
|
||||
|
||||
for (var c = 0; c < ngHelpers.classTypes.length; c++) {
|
||||
var classType = lib[ngHelpers.classTypes[c]] = {};
|
||||
|
||||
classType.undocumented = [];
|
||||
classType.documented = [];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function buildMDDocumentedTable(docItems, forSubFolder) {
|
||||
var rows = [
|
||||
|
||||
];
|
||||
|
||||
for (var i = 0; i < docItems.length; i++) {
|
||||
rows.push(makeMDDocumentedTableRow(docItems[i], forSubFolder));
|
||||
}
|
||||
|
||||
return rows;
|
||||
//return unist.makeTable([null, null, null, null], rows);
|
||||
}
|
||||
|
||||
|
||||
function buildMDUndocumentedTable(docItems, forSubFolder) {
|
||||
var rows = [
|
||||
|
||||
];
|
||||
|
||||
for (var i = 0; i < docItems.length; i++) {
|
||||
rows.push(makeMDUndocumentedTableRow(docItems[i], forSubFolder));
|
||||
}
|
||||
|
||||
return rows;
|
||||
//return unist.makeTable([null, null, null, null], rows);
|
||||
}
|
||||
|
||||
|
||||
function makeMDDocumentedTableRow(docItem, forSubFolder) {
|
||||
var mdPath = docItem.mdPath;
|
||||
|
||||
if (forSubFolder) {
|
||||
mdPath = path.basename(mdPath);
|
||||
}
|
||||
|
||||
var mdFileLink = unist.makeLink(unist.makeText(docItem.displayName), mdPath);
|
||||
|
||||
var srcPath = "../lib/" + docItem.srcPath;
|
||||
|
||||
if (forSubFolder) {
|
||||
srcPath = "../" + srcPath;
|
||||
}
|
||||
|
||||
var srcFileLink = unist.makeLink(unist.makeText("Source"), srcPath);
|
||||
var desc = docItem.briefDesc;
|
||||
|
||||
var linkCellItems = [mdFileLink];
|
||||
|
||||
var finalDepIconURL = deprecatedIconURL;
|
||||
var finalExIconURL = experimentalIconURL;
|
||||
|
||||
if (forSubFolder) {
|
||||
finalDepIconURL = "../" + finalDepIconURL;
|
||||
finalExIconURL = "../" + finalExIconURL;
|
||||
}
|
||||
|
||||
if (docItem.status) {
|
||||
if (docItem.status === "Deprecated") {
|
||||
linkCellItems.push(unist.makeText(" "));
|
||||
linkCellItems.push(unist.makeImage(finalDepIconURL, "Deprecated"));
|
||||
} else if (docItem.status === "Experimental") {
|
||||
linkCellItems.push(unist.makeText(" "));
|
||||
linkCellItems.push(unist.makeImage(finalExIconURL, "Experimental"));
|
||||
}
|
||||
}
|
||||
|
||||
return unist.makeTableRow([
|
||||
unist.makeTableCell(linkCellItems),
|
||||
unist.makeTableCell([desc]),
|
||||
unist.makeTableCell([srcFileLink])
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
function makeMDUndocumentedTableRow(docItem, forSubFolder) {
|
||||
var itemName = unist.makeText(docItem.displayName);
|
||||
var srcPath = "../lib/" + docItem.srcPath;
|
||||
|
||||
if (forSubFolder) {
|
||||
srcPath = "../" + srcPath;
|
||||
}
|
||||
|
||||
var srcFileLink = unist.makeLink(unist.makeText("Source"), srcPath);
|
||||
|
||||
return unist.makeTableRow([
|
||||
unist.makeTableCell([unist.makeEmphasis([itemName])]),
|
||||
unist.makeTableCell([unist.makeEmphasis([unist.makeText("Not currently documented")])]),
|
||||
unist.makeTableCell([srcFileLink])
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
function makeLibSectionMD(libSection, forSubFolder){
|
||||
var md = [];
|
||||
|
||||
var libClassTypes = Object.keys(libSection);
|
||||
|
||||
for (var i = 0; i < libClassTypes.length; i++) {
|
||||
var classType = libClassTypes[i];
|
||||
|
||||
var classSection = libSection[classType];
|
||||
|
||||
if (!classSection)
|
||||
continue;
|
||||
|
||||
var displayNameNode;
|
||||
|
||||
if ((classSection.documented.length > 0) || (classSection.undocumented.length > 0)) {
|
||||
displayNameNode = unist.makeText(ngHelpers.dekebabifyName(classType + "s"));
|
||||
md.push(unist.makeHeading(displayNameNode, 2));
|
||||
|
||||
var tableRows = [
|
||||
unist.makeTableRow([
|
||||
unist.makeTableCell([unist.makeText("Name")]),
|
||||
unist.makeTableCell([unist.makeText("Description")]),
|
||||
unist.makeTableCell([unist.makeText("Source link")])
|
||||
])
|
||||
];
|
||||
|
||||
if (classSection.documented.length > 0) {
|
||||
//md.push(buildMDDocumentedSection(classSection.documented));
|
||||
tableRows = tableRows.concat(buildMDDocumentedTable(classSection.documented, forSubFolder));
|
||||
}
|
||||
|
||||
if (classSection.undocumented.length > 0) {
|
||||
// md.push(buildMDUndocumentedSection(classSection.undocumented));
|
||||
tableRows = tableRows.concat(buildMDUndocumentedTable(classSection.undocumented, forSubFolder));
|
||||
}
|
||||
|
||||
md.push(unist.makeTable([null, null, null, null], tableRows));
|
||||
}
|
||||
}
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
|
||||
function buildGuideSection(guideJsonFilename, forSubFolder) {
|
||||
var summary = JSON.parse(fs.readFileSync(guideJsonFilename, "utf8"));
|
||||
|
||||
var listItems = [];
|
||||
|
||||
for (var i = 0; i < summary.length; i++) {
|
||||
var filePath = summary[i].file;
|
||||
|
||||
if (!forSubFolder) {
|
||||
filePath = guideFolderName + "/" + filePath;
|
||||
}
|
||||
|
||||
if (summary[i].title !== "Tutorials") {
|
||||
var link = unist.makeLink(unist.makeText(summary[i].title), filePath);
|
||||
listItems.push(unist.makeListItem(link));
|
||||
}
|
||||
}
|
||||
|
||||
return unist.makeListUnordered(listItems);
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
var path = require("path");
|
||||
var getSection = require("mdast-util-heading-range");
|
||||
|
||||
var unist = require("../unistHelpers");
|
||||
|
||||
var seeAlsoHeading = "See Also";
|
||||
|
||||
module.exports = {
|
||||
"initPhase": initPhase,
|
||||
"readPhase": readPhase,
|
||||
"aggPhase": aggPhase,
|
||||
"updatePhase": updatePhase
|
||||
}
|
||||
|
||||
|
||||
function initPhase(aggData) {
|
||||
aggData.saGraph = {};
|
||||
aggData.saUpdateGraph = {};
|
||||
}
|
||||
|
||||
|
||||
function readPhase(tree, pathname, aggData) {
|
||||
var saHeadingOffset = findSeeAlsoSection(tree);
|
||||
|
||||
var saNode = [];
|
||||
|
||||
if (saHeadingOffset !== -1) {
|
||||
// Skip over non-list parts.
|
||||
var s;
|
||||
|
||||
for (
|
||||
s = saHeadingOffset;
|
||||
(s < tree.children.length) && !unist.isListUnordered(tree.children[s]);
|
||||
s++
|
||||
);
|
||||
|
||||
if ((s < tree.children.length) && unist.isListUnordered(tree.children[s])) {
|
||||
var list = tree.children[s];
|
||||
|
||||
for (var i = 0; i < list.children.length; i++) {
|
||||
var itemLink = getItemLinkInfo(list.children[i]);
|
||||
|
||||
if (itemLink) {
|
||||
saNode.push(itemLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aggData.saGraph[path.basename(pathname, ".md")] = saNode;
|
||||
}
|
||||
|
||||
|
||||
function aggPhase(aggData) {
|
||||
aggData.saUpdateGraph = tidyGraph(aggData.saGraph);
|
||||
}
|
||||
|
||||
|
||||
function updatePhase(tree, pathname, aggData) {
|
||||
var currNodeName = path.basename(pathname, ".md");
|
||||
var currNodeArcs = aggData.saUpdateGraph[currNodeName];
|
||||
|
||||
if (currNodeArcs.length > 0) {
|
||||
var saListItems = [];
|
||||
|
||||
for (var i = 0; i < currNodeArcs.length; i++) {
|
||||
var linkText = graphKeyToLinkName(currNodeArcs[i]);
|
||||
var linkTarget = currNodeArcs[i] + ".md";
|
||||
var link = unist.makeLink(unist.makeText(linkText), linkTarget);
|
||||
saListItems.push(unist.makeListItem(link));
|
||||
}
|
||||
|
||||
var saHeadingOffset = findSeeAlsoSection(tree);
|
||||
|
||||
if (saHeadingOffset !== -1) {
|
||||
// Skip over non-list parts.
|
||||
var s;
|
||||
|
||||
for (
|
||||
s = saHeadingOffset;
|
||||
(s < tree.children.length) && !unist.isListUnordered(tree.children[s]);
|
||||
s++
|
||||
);
|
||||
|
||||
// Push all elements of the items array as if they were separate elements.
|
||||
Array.prototype.push.apply(tree.children[s].children, saListItems);
|
||||
} else {
|
||||
// This file doesn't currently have a See Also section, so add one at the end.
|
||||
tree.children.push(unist.makeHeading(unist.makeText(seeAlsoHeading), 2));
|
||||
tree.children.push(unist.makeListUnordered(saListItems));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function graphKeyToLinkName(key) {
|
||||
var mainSections = key.split(".");
|
||||
mainSections[0] = tidyName(mainSections[0]);
|
||||
return mainSections.join(" ");
|
||||
}
|
||||
|
||||
// Convert an Angular-style name (eg, "card-view") into one with correct spaces and uppercase (eg, "Card view").
|
||||
function tidyName(name) {
|
||||
var result = name.replace(/-/g, " ");
|
||||
result = result.substr(0, 1).toUpperCase() + result.substr(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function makeEmptySAList() {
|
||||
var result = [];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Makes link symmetrical between items (ie, if A links to B but not the other way
|
||||
// around then it adds the missing link).
|
||||
function tidyGraph(graph) {
|
||||
var nodeNames = Object.keys(graph);
|
||||
var result = {};
|
||||
|
||||
for (var n = 0; n < nodeNames.length; n++) {
|
||||
result[nodeNames[n]] = [];
|
||||
}
|
||||
|
||||
for (var n = 0; n < nodeNames.length; n++) {
|
||||
var currNodeName = nodeNames[n];
|
||||
|
||||
var currNodeArcs = graph[currNodeName];
|
||||
|
||||
for (var a = 0; a < currNodeArcs.length; a++) {
|
||||
var linkedNode = graph[currNodeArcs[a]];
|
||||
var resultNode = result[currNodeArcs[a]];
|
||||
|
||||
if (!linkedNode) {
|
||||
console.log(`Warning: item '${currNodeArcs[a]}' (in See Also section of '${currNodeName}') has no corresponding file`);
|
||||
} else if (linkedNode.indexOf(currNodeName) === -1) {
|
||||
linkedNode.push(currNodeName);
|
||||
resultNode.push(currNodeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function findSeeAlsoSection(tree) {
|
||||
var i;
|
||||
|
||||
for (i = 0; i < tree.children.length; i++) {
|
||||
var child = tree.children[i];
|
||||
|
||||
if (unist.isHeading(child) && (child.children[0].value.toLowerCase() === seeAlsoHeading.toLowerCase()))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
function getItemLinkInfo(listItem) {
|
||||
var linkTarget = listItem.children[0].children[0].url;
|
||||
|
||||
if (linkTarget.startsWith("http:") ||
|
||||
linkTarget.startsWith("#") ||
|
||||
!linkTarget.endsWith(".md"))
|
||||
return null;
|
||||
else
|
||||
return path.basename(linkTarget, ".md");
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
var remark = require("remark");
|
||||
var tocGenerator = require("mdast-util-toc");
|
||||
var replaceSection = require("mdast-util-heading-range");
|
||||
|
||||
var unist = require("../unistHelpers");
|
||||
|
||||
const contentsHeading = "Contents";
|
||||
const minHeadingsForToc = 8;
|
||||
const maxTocHeadingDepth = 3;
|
||||
|
||||
module.exports = {
|
||||
"initPhase": initPhase,
|
||||
"readPhase": readPhase,
|
||||
"aggPhase": aggPhase,
|
||||
"updatePhase": updatePhase
|
||||
}
|
||||
|
||||
function initPhase(aggData) {}
|
||||
function readPhase(tree, pathname, aggData) {}
|
||||
function aggPhase(aggData) {}
|
||||
|
||||
|
||||
// Find an existing Contents section or add a new empty one if needed.
|
||||
// Returns true if section is present/needed, false if not needed.
|
||||
function establishContentsSection(mdTree) {
|
||||
var firstL2HeadingPos = -1;
|
||||
var numTocHeadings = 0;
|
||||
var foundContentsHeading = false;
|
||||
|
||||
for (var i = 0; i < mdTree.children.length; i++) {
|
||||
var child = mdTree.children[i];
|
||||
|
||||
// Look through all headings.
|
||||
if (child.type === "heading") {
|
||||
|
||||
if ((child.depth > 1) && (child.depth <= maxTocHeadingDepth)) {
|
||||
numTocHeadings++;
|
||||
}
|
||||
|
||||
if (child.depth === 2) {
|
||||
// Note where the first L2 heading is.
|
||||
if (firstL2HeadingPos === -1) {
|
||||
firstL2HeadingPos = i;
|
||||
}
|
||||
|
||||
// If it is also a Contents heading then we're done. We don't include the
|
||||
// Contents heading itself within the ToC, so decrement the count for that.
|
||||
if ((child.children[0].value === contentsHeading) && !foundContentsHeading) {
|
||||
foundContentsHeading = true;
|
||||
numTocHeadings--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here then a level 2 Contents heading was not found.
|
||||
// If there are enough headings for a ToC to be necessary then
|
||||
// add one in the right place.
|
||||
if (!foundContentsHeading) {
|
||||
var newContsHeading = unist.makeHeading(unist.makeText(contentsHeading), 2);
|
||||
|
||||
// If we found another L2 heading then add the Contents in just before it.
|
||||
if (firstL2HeadingPos != -1) {
|
||||
mdTree.children.splice(firstL2HeadingPos, 0, newContsHeading);
|
||||
} else {
|
||||
// Otherwise, the unlikely situation where a ToC is required but there
|
||||
// are no L2 headings! Add it as the second element in the document.
|
||||
mdTree.children.splice(1, 0, newContsHeading);
|
||||
}
|
||||
}
|
||||
|
||||
return numTocHeadings;
|
||||
}
|
||||
|
||||
function updatePhase(tree, pathname, aggData) {
|
||||
// If we need a contents section then add one or update the existing one.
|
||||
var numTocHeadings = establishContentsSection(tree);
|
||||
|
||||
if (numTocHeadings >= minHeadingsForToc) {
|
||||
var newToc = tocGenerator(tree, {heading: contentsHeading, maxDepth: 3});
|
||||
|
||||
replaceSection(tree, contentsHeading, function(before, oldSection, after) {
|
||||
return [before, newToc.map, after];
|
||||
});
|
||||
} else {
|
||||
// Otherwise, we don't need one, so remove any existing one.
|
||||
replaceSection(tree, contentsHeading, function(before, oldSection, after) {
|
||||
return [after];
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var replaceSection = require("mdast-util-heading-range");
|
||||
var remark = require("remark");
|
||||
var combyne = require("combyne");
|
||||
var typedoc_1 = require("typedoc");
|
||||
var libFolders = ["core", "content-services", "process-services", "insights"];
|
||||
var templateFolder = path.resolve(".", "config", "DocProcessor", "templates");
|
||||
var excludePatterns = [
|
||||
"**/*.spec.ts"
|
||||
];
|
||||
var nameExceptions = {
|
||||
"datatable.component": "DataTableComponent",
|
||||
"tasklist.service": "TaskListService",
|
||||
"text-mask.component": "InputMaskDirective"
|
||||
};
|
||||
var PropInfo = /** @class */ (function () {
|
||||
function PropInfo(rawProp) {
|
||||
var _this = this;
|
||||
this.name = rawProp.name;
|
||||
this.docText = rawProp.comment ? rawProp.comment.shortText : "";
|
||||
this.docText = this.docText.replace(/[\n\r]+/g, " ").trim();
|
||||
this.defaultValue = rawProp.defaultValue || "";
|
||||
this.defaultValue = this.defaultValue.replace(/\|/, "\\|");
|
||||
this.type = rawProp.type ? rawProp.type.toString() : "";
|
||||
this.isDeprecated = rawProp.comment && rawProp.comment.hasTag("deprecated");
|
||||
if (this.isDeprecated) {
|
||||
this.docText = "**Deprecated:** " + rawProp.comment.getTag("deprecated").text.replace(/[\n\r]+/g, " ").trim();
|
||||
}
|
||||
if (rawProp.decorators) {
|
||||
rawProp.decorators.forEach(function (dec) {
|
||||
//console.log(dec);
|
||||
if (dec.name === "Input") {
|
||||
_this.isInput = true;
|
||||
if (dec.arguments) {
|
||||
var bindingName = dec.arguments["bindingPropertyName"];
|
||||
//console.log(JSON.stringify(dec.arguments));
|
||||
if (bindingName && (bindingName !== ""))
|
||||
_this.name = bindingName.replace(/['"]/g, "");
|
||||
}
|
||||
if (!_this.docText && !_this.isDeprecated)
|
||||
console.log("Warning: Input \"" + rawProp.getFullName() + "\" has no doc text.");
|
||||
}
|
||||
if (dec.name === "Output") {
|
||||
_this.isOutput = true;
|
||||
if (!_this.docText && !_this.isDeprecated)
|
||||
console.log("Warning: Output \"" + rawProp.getFullName() + "\" has no doc text.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return PropInfo;
|
||||
}());
|
||||
;
|
||||
var ParamInfo = /** @class */ (function () {
|
||||
function ParamInfo(rawParam) {
|
||||
this.name = rawParam.name;
|
||||
this.type = rawParam.type.toString();
|
||||
this.defaultValue = rawParam.defaultValue;
|
||||
this.docText = rawParam.comment ? rawParam.comment.text : "";
|
||||
this.docText = this.docText.replace(/[\n\r]+/g, " ").trim();
|
||||
this.isOptional = rawParam.flags.isOptional;
|
||||
this.combined = this.name;
|
||||
if (this.isOptional)
|
||||
this.combined += "?";
|
||||
this.combined += ": " + this.type;
|
||||
if (this.defaultValue !== "")
|
||||
this.combined += " = " + this.defaultValue;
|
||||
}
|
||||
return ParamInfo;
|
||||
}());
|
||||
var MethodSigInfo = /** @class */ (function () {
|
||||
function MethodSigInfo(rawSig) {
|
||||
var _this = this;
|
||||
this.name = rawSig.name;
|
||||
this.returnType = rawSig.type ? rawSig.type.toString() : "";
|
||||
this.returnsSomething = this.returnType != "void";
|
||||
if (rawSig.hasComment()) {
|
||||
this.docText = rawSig.comment.shortText + rawSig.comment.text;
|
||||
this.docText = this.docText.replace(/[\n\r]+/g, " ").trim();
|
||||
if (!this.docText)
|
||||
console.log("Warning: method \"" + rawSig.name + "\" has no doc text.");
|
||||
this.returnDocText = rawSig.comment.returns;
|
||||
this.returnDocText = this.returnDocText ? this.returnDocText.replace(/[\n\r]+/g, " ").trim() : "";
|
||||
if (this.returnDocText.toLowerCase() === "nothing")
|
||||
this.returnsSomething = false;
|
||||
if (this.returnsSomething && !this.returnDocText)
|
||||
console.log("Warning: Return value of method \"" + rawSig.name + "\" has no doc text.");
|
||||
this.isDeprecated = rawSig.comment.hasTag("deprecated");
|
||||
}
|
||||
this.params = [];
|
||||
var paramStrings = [];
|
||||
if (rawSig.parameters) {
|
||||
rawSig.parameters.forEach(function (rawParam) {
|
||||
if (!rawParam.comment || !rawParam.comment.text)
|
||||
console.log("Warning: parameter \"" + rawParam.name + "\" of method \"" + rawSig.name + "\" has no doc text.");
|
||||
var param = new ParamInfo(rawParam);
|
||||
_this.params.push(param);
|
||||
paramStrings.push(param.combined);
|
||||
});
|
||||
}
|
||||
this.signature = "(" + paramStrings.join(", ") + ")";
|
||||
}
|
||||
return MethodSigInfo;
|
||||
}());
|
||||
var ComponentInfo = /** @class */ (function () {
|
||||
function ComponentInfo(classRef) {
|
||||
var _this = this;
|
||||
var props = classRef.getChildrenByKind(typedoc_1.ReflectionKind.Property);
|
||||
var accessors = classRef.getChildrenByKind(typedoc_1.ReflectionKind.Accessor);
|
||||
this.properties = props.concat(accessors).map(function (item) {
|
||||
return new PropInfo(item);
|
||||
});
|
||||
var methods = classRef.getChildrenByKind(typedoc_1.ReflectionKind.Method);
|
||||
this.methods = [];
|
||||
methods.forEach(function (method) {
|
||||
if (!(method.flags.isPrivate || method.flags.isProtected)) {
|
||||
method.signatures.forEach(function (sig) {
|
||||
_this.methods.push(new MethodSigInfo(sig));
|
||||
});
|
||||
}
|
||||
});
|
||||
this.properties.forEach(function (prop) {
|
||||
if (prop.isInput)
|
||||
_this.hasInputs = true;
|
||||
if (prop.isOutput)
|
||||
_this.hasOutputs = true;
|
||||
});
|
||||
this.hasMethods = methods.length > 0;
|
||||
}
|
||||
return ComponentInfo;
|
||||
}());
|
||||
function initPhase(aggData) {
|
||||
var app = new typedoc_1.Application({
|
||||
exclude: excludePatterns,
|
||||
ignoreCompilerErrors: true,
|
||||
experimentalDecorators: true,
|
||||
tsconfig: "tsconfig.json"
|
||||
});
|
||||
var sources = app.expandInputFiles(libFolders);
|
||||
aggData.projData = app.convert(sources);
|
||||
/*
|
||||
aggData.liq = liquid({
|
||||
root: templateFolder
|
||||
});
|
||||
*/
|
||||
}
|
||||
exports.initPhase = initPhase;
|
||||
function readPhase(tree, pathname, aggData) {
|
||||
}
|
||||
exports.readPhase = readPhase;
|
||||
function aggPhase(aggData) {
|
||||
}
|
||||
exports.aggPhase = aggPhase;
|
||||
function updatePhase(tree, pathname, aggData) {
|
||||
var compName = angNameToClassName(path.basename(pathname, ".md"));
|
||||
var classRef = aggData.projData.findReflectionByName(compName);
|
||||
var compData = new ComponentInfo(classRef);
|
||||
var classType = compName.match(/component|directive|service/i);
|
||||
if (classType) {
|
||||
var templateName = path.resolve(templateFolder, classType + ".combyne");
|
||||
var templateSource = fs.readFileSync(templateName, "utf8");
|
||||
var template = combyne(templateSource);
|
||||
var mdText = template.render(compData);
|
||||
mdText = mdText.replace(/^ +\|/mg, "|");
|
||||
var newSection_1 = remark().data("settings", { paddedTable: false, gfm: false }).parse(mdText.trim()).children;
|
||||
replaceSection(tree, "Class members", function (before, section, after) {
|
||||
newSection_1.unshift(before);
|
||||
newSection_1.push(after);
|
||||
return newSection_1;
|
||||
});
|
||||
/*
|
||||
let templateName = classType[0] + ".liquid";
|
||||
|
||||
aggData.liq
|
||||
.renderFile(templateName, compData)
|
||||
.then(mdText => {
|
||||
let newSection = remark().parse(mdText).children;
|
||||
replaceSection(tree, "Class members", (before, section, after) => {
|
||||
newSection.unshift(before);
|
||||
newSection.push(after);
|
||||
return newSection;
|
||||
});
|
||||
|
||||
fs.writeFileSync(pathname, remark().use(frontMatter, {type: 'yaml', fence: '---'}).data("settings", {paddedTable: false}).stringify(tree));
|
||||
|
||||
});
|
||||
*/
|
||||
}
|
||||
return true;
|
||||
}
|
||||
exports.updatePhase = updatePhase;
|
||||
/*
|
||||
function renderInputs(comp: ComponentInfo): string {
|
||||
var result = "";
|
||||
|
||||
comp.properties.forEach(prop => {
|
||||
result += `| ${prop.name} | \`${prop.type}\` | ${prop.defaultValue} | ${prop.docText} |\n`;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
function initialCap(str) {
|
||||
return str[0].toUpperCase() + str.substr(1);
|
||||
}
|
||||
function angNameToClassName(rawName) {
|
||||
if (nameExceptions[rawName])
|
||||
return nameExceptions[rawName];
|
||||
var name = rawName.replace(/\]|\(|\)/g, '');
|
||||
var fileNameSections = name.split('.');
|
||||
var compNameSections = fileNameSections[0].split('-');
|
||||
var outCompName = '';
|
||||
for (var i = 0; i < compNameSections.length; i++) {
|
||||
outCompName = outCompName + initialCap(compNameSections[i]);
|
||||
}
|
||||
var itemTypeIndicator = '';
|
||||
if (fileNameSections.length > 1) {
|
||||
itemTypeIndicator = initialCap(fileNameSections[1]);
|
||||
}
|
||||
var finalName = outCompName + itemTypeIndicator;
|
||||
return finalName;
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import * as replaceSection from "mdast-util-heading-range";
|
||||
import * as remark from "remark";
|
||||
import * as stringify from "remark-stringify";
|
||||
import * as frontMatter from "remark-frontmatter";
|
||||
|
||||
import * as combyne from "combyne";
|
||||
|
||||
import {
|
||||
Application,
|
||||
ProjectReflection,
|
||||
Reflection,
|
||||
DeclarationReflection,
|
||||
SignatureReflection,
|
||||
ParameterReflection,
|
||||
ReflectionKind,
|
||||
TraverseProperty,
|
||||
Decorator
|
||||
} from "typedoc";
|
||||
import { CommentTag } from "typedoc/dist/lib/models";
|
||||
|
||||
|
||||
let libFolders = ["core", "content-services", "process-services", "insights"];
|
||||
let templateFolder = path.resolve(".", "config", "DocProcessor", "templates");
|
||||
|
||||
let excludePatterns = [
|
||||
"**/*.spec.ts"
|
||||
];
|
||||
|
||||
|
||||
let nameExceptions = {
|
||||
"datatable.component": "DataTableComponent",
|
||||
"tasklist.service": "TaskListService",
|
||||
"text-mask.component": "InputMaskDirective"
|
||||
}
|
||||
|
||||
|
||||
class PropInfo {
|
||||
name: string;
|
||||
type: string;
|
||||
typeLink: string;
|
||||
defaultValue: string;
|
||||
docText: string;
|
||||
isInput: boolean;
|
||||
isOutput: boolean;
|
||||
isDeprecated: boolean;
|
||||
|
||||
constructor(rawProp: DeclarationReflection) {
|
||||
this.name = rawProp.name;
|
||||
this.docText = rawProp.comment ? rawProp.comment.shortText : "";
|
||||
this.docText = this.docText.replace(/[\n\r]+/g, " ").trim();
|
||||
this.defaultValue = rawProp.defaultValue || "";
|
||||
this.defaultValue = this.defaultValue.replace(/\|/, "\\|");
|
||||
this.type = rawProp.type ? rawProp.type.toString() : "";
|
||||
|
||||
this.isDeprecated = rawProp.comment && rawProp.comment.hasTag("deprecated");
|
||||
|
||||
if (this.isDeprecated) {
|
||||
this.docText = "**Deprecated:** " + rawProp.comment.getTag("deprecated").text.replace(/[\n\r]+/g, " ").trim();
|
||||
}
|
||||
|
||||
if (rawProp.decorators) {
|
||||
rawProp.decorators.forEach(dec => {
|
||||
//console.log(dec);
|
||||
if (dec.name === "Input") {
|
||||
this.isInput = true;
|
||||
|
||||
if (dec.arguments) {
|
||||
let bindingName = dec.arguments["bindingPropertyName"];
|
||||
//console.log(JSON.stringify(dec.arguments));
|
||||
|
||||
if (bindingName && (bindingName !== ""))
|
||||
this.name = bindingName.replace(/['"]/g, "");
|
||||
}
|
||||
|
||||
if (!this.docText && !this.isDeprecated)
|
||||
console.log(`Warning: Input "${rawProp.getFullName()}" has no doc text.`);
|
||||
}
|
||||
|
||||
if (dec.name === "Output") {
|
||||
this.isOutput = true;
|
||||
|
||||
if (!this.docText && !this.isDeprecated)
|
||||
console.log(`Warning: Output "${rawProp.getFullName()}" has no doc text.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ParamInfo {
|
||||
name: string;
|
||||
type: string;
|
||||
defaultValue: string;
|
||||
docText: string;
|
||||
combined: string;
|
||||
isOptional: boolean;
|
||||
|
||||
constructor(rawParam: ParameterReflection) {
|
||||
this.name = rawParam.name;
|
||||
this.type = rawParam.type.toString();
|
||||
this.defaultValue = rawParam.defaultValue;
|
||||
this.docText = rawParam.comment ? rawParam.comment.text : "";
|
||||
this.docText = this.docText.replace(/[\n\r]+/g, " ").trim();
|
||||
this.isOptional = rawParam.flags.isOptional;
|
||||
|
||||
this.combined = this.name;
|
||||
|
||||
if (this.isOptional)
|
||||
this.combined += "?";
|
||||
|
||||
this.combined += `: ${this.type}`;
|
||||
|
||||
if (this.defaultValue !== "")
|
||||
this.combined += ` = ${this.defaultValue}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MethodSigInfo {
|
||||
name: string;
|
||||
docText: string;
|
||||
returnType: string;
|
||||
returnDocText: string;
|
||||
returnsSomething: boolean;
|
||||
signature: string;
|
||||
params: ParamInfo[];
|
||||
isDeprecated: boolean;
|
||||
|
||||
constructor(rawSig: SignatureReflection) {
|
||||
this.name = rawSig.name;
|
||||
this.returnType = rawSig.type ? rawSig.type.toString() : "";
|
||||
this.returnsSomething = this.returnType != "void";
|
||||
|
||||
if (rawSig.hasComment()) {
|
||||
this.docText = rawSig.comment.shortText + rawSig.comment.text;
|
||||
this.docText = this.docText.replace(/[\n\r]+/g, " ").trim();
|
||||
|
||||
if (!this.docText)
|
||||
console.log(`Warning: method "${rawSig.name}" has no doc text.`);
|
||||
|
||||
this.returnDocText = rawSig.comment.returns;
|
||||
this.returnDocText = this.returnDocText ? this.returnDocText.replace(/[\n\r]+/g, " ").trim() : "";
|
||||
|
||||
if (this.returnDocText.toLowerCase() === "nothing")
|
||||
this.returnsSomething = false;
|
||||
|
||||
if (this.returnsSomething && !this.returnDocText)
|
||||
console.log(`Warning: Return value of method "${rawSig.name}" has no doc text.`);
|
||||
|
||||
this.isDeprecated = rawSig.comment.hasTag("deprecated");
|
||||
}
|
||||
|
||||
this.params = [];
|
||||
let paramStrings = [];
|
||||
|
||||
if (rawSig.parameters) {
|
||||
rawSig.parameters.forEach(rawParam => {
|
||||
if (!rawParam.comment || !rawParam.comment.text)
|
||||
console.log(`Warning: parameter "${rawParam.name}" of method "${rawSig.name}" has no doc text.`);
|
||||
let param = new ParamInfo(rawParam);
|
||||
this.params.push(param);
|
||||
paramStrings.push(param.combined);
|
||||
});
|
||||
}
|
||||
|
||||
this.signature = "(" + paramStrings.join(", ") + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ComponentInfo {
|
||||
properties: PropInfo[];
|
||||
methods: MethodSigInfo[];
|
||||
hasInputs: boolean;
|
||||
hasOutputs: boolean;
|
||||
hasMethods: boolean;
|
||||
|
||||
constructor(classRef: DeclarationReflection) {
|
||||
let props = classRef.getChildrenByKind(ReflectionKind.Property);
|
||||
let accessors = classRef.getChildrenByKind(ReflectionKind.Accessor);
|
||||
|
||||
this.properties = [...props, ...accessors].map(item => {
|
||||
return new PropInfo(item);
|
||||
});
|
||||
|
||||
let methods = classRef.getChildrenByKind(ReflectionKind.Method);
|
||||
|
||||
this.methods = [];
|
||||
|
||||
methods.forEach(method =>{
|
||||
if (!(method.flags.isPrivate || method.flags.isProtected)) {
|
||||
method.signatures.forEach(sig => {
|
||||
this.methods.push(new MethodSigInfo(sig));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.properties.forEach(prop => {
|
||||
if (prop.isInput)
|
||||
this.hasInputs = true;
|
||||
|
||||
if (prop.isOutput)
|
||||
this.hasOutputs = true;
|
||||
});
|
||||
|
||||
this.hasMethods = methods.length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function initPhase(aggData) {
|
||||
let app = new Application({
|
||||
exclude: excludePatterns,
|
||||
ignoreCompilerErrors: true,
|
||||
experimentalDecorators: true,
|
||||
tsconfig: "tsconfig.json"
|
||||
});
|
||||
|
||||
let sources = app.expandInputFiles(libFolders);
|
||||
aggData.projData = app.convert(sources);
|
||||
|
||||
/*
|
||||
aggData.liq = liquid({
|
||||
root: templateFolder
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
export function readPhase(tree, pathname, aggData) {
|
||||
}
|
||||
|
||||
|
||||
export function aggPhase(aggData) {
|
||||
}
|
||||
|
||||
|
||||
export function updatePhase(tree, pathname, aggData) {
|
||||
let compName = angNameToClassName(path.basename(pathname, ".md"));
|
||||
let classRef = aggData.projData.findReflectionByName(compName);
|
||||
let compData = new ComponentInfo(classRef);
|
||||
let classType = compName.match(/component|directive|service/i);
|
||||
|
||||
if (classType) {
|
||||
let templateName = path.resolve(templateFolder, classType + ".combyne");
|
||||
let templateSource = fs.readFileSync(templateName, "utf8");
|
||||
let template = combyne(templateSource);
|
||||
|
||||
let mdText = template.render(compData);
|
||||
mdText = mdText.replace(/^ +\|/mg, "|");
|
||||
|
||||
let newSection = remark().data("settings", {paddedTable: false, gfm: false}).parse(mdText.trim()).children;
|
||||
|
||||
replaceSection(tree, "Class members", (before, section, after) => {
|
||||
newSection.unshift(before);
|
||||
newSection.push(after);
|
||||
return newSection;
|
||||
});
|
||||
/*
|
||||
let templateName = classType[0] + ".liquid";
|
||||
|
||||
aggData.liq
|
||||
.renderFile(templateName, compData)
|
||||
.then(mdText => {
|
||||
let newSection = remark().parse(mdText).children;
|
||||
replaceSection(tree, "Class members", (before, section, after) => {
|
||||
newSection.unshift(before);
|
||||
newSection.push(after);
|
||||
return newSection;
|
||||
});
|
||||
|
||||
fs.writeFileSync(pathname, remark().use(frontMatter, {type: 'yaml', fence: '---'}).data("settings", {paddedTable: false}).stringify(tree));
|
||||
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
function renderInputs(comp: ComponentInfo): string {
|
||||
var result = "";
|
||||
|
||||
comp.properties.forEach(prop => {
|
||||
result += `| ${prop.name} | \`${prop.type}\` | ${prop.defaultValue} | ${prop.docText} |\n`;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
function initialCap(str: string) {
|
||||
return str[0].toUpperCase() + str.substr(1);
|
||||
}
|
||||
|
||||
|
||||
function angNameToClassName(rawName: string) {
|
||||
if (nameExceptions[rawName])
|
||||
return nameExceptions[rawName];
|
||||
|
||||
var name = rawName.replace(/\]|\(|\)/g, '');
|
||||
|
||||
var fileNameSections = name.split('.');
|
||||
var compNameSections = fileNameSections[0].split('-');
|
||||
|
||||
var outCompName = '';
|
||||
|
||||
for (var i = 0; i < compNameSections.length; i++) {
|
||||
outCompName = outCompName + initialCap(compNameSections[i]);
|
||||
}
|
||||
|
||||
var itemTypeIndicator = '';
|
||||
|
||||
if (fileNameSections.length > 1) {
|
||||
itemTypeIndicator = initialCap(fileNameSections[1]);
|
||||
}
|
||||
|
||||
var finalName = outCompName + itemTypeIndicator;
|
||||
|
||||
return finalName;
|
||||
}
|
||||
@@ -1,357 +0,0 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var ts = require("typescript");
|
||||
var path = require("path");
|
||||
var heading = require("mdast-util-heading-range");
|
||||
var remark = require("remark");
|
||||
var unist = require("../unistHelpers");
|
||||
var typescript_1 = require("typescript");
|
||||
// Max number of characters in the text for the default value column.
|
||||
var maxDefaultTextLength = 20;
|
||||
var nameExceptions = {
|
||||
"datatable.component": "DataTableComponent",
|
||||
"tasklist.service": "TaskListService"
|
||||
};
|
||||
function initPhase(aggData) {
|
||||
}
|
||||
exports.initPhase = initPhase;
|
||||
function readPhase(tree, pathname, aggData) {
|
||||
}
|
||||
exports.readPhase = readPhase;
|
||||
function aggPhase(aggData) {
|
||||
}
|
||||
exports.aggPhase = aggPhase;
|
||||
var PropData = /** @class */ (function () {
|
||||
function PropData() {
|
||||
}
|
||||
return PropData;
|
||||
}());
|
||||
var ParamData = /** @class */ (function () {
|
||||
function ParamData() {
|
||||
}
|
||||
ParamData.prototype.getSignature = function () {
|
||||
var sig = this.name;
|
||||
if (this.optional)
|
||||
sig += "?";
|
||||
if (this.type)
|
||||
sig += ": " + this.type;
|
||||
if (this.initializer)
|
||||
sig += " = " + this.initializer;
|
||||
return sig;
|
||||
};
|
||||
return ParamData;
|
||||
}());
|
||||
var MethodData = /** @class */ (function () {
|
||||
function MethodData() {
|
||||
this.params = [];
|
||||
}
|
||||
MethodData.prototype.getSignature = function () {
|
||||
var sig = this.name + "(";
|
||||
if (this.params.length > 0) {
|
||||
sig += this.params[0].getSignature();
|
||||
}
|
||||
for (var i = 1; i < this.params.length; i++) {
|
||||
sig += ", " + this.params[i].getSignature();
|
||||
}
|
||||
sig += ")";
|
||||
if (this.returnType !== "void") {
|
||||
sig += ": " + this.returnType;
|
||||
}
|
||||
return sig;
|
||||
};
|
||||
return MethodData;
|
||||
}());
|
||||
var ComponentDocAutoContent = /** @class */ (function () {
|
||||
function ComponentDocAutoContent() {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
}
|
||||
ComponentDocAutoContent.prototype.extractClassInfoFromSource = function (checker, classDec) {
|
||||
var sourceFile = classDec.getSourceFile();
|
||||
for (var i = 0; i < classDec.members.length; i++) {
|
||||
var member = classDec.members[i];
|
||||
if (ts.isPropertyDeclaration(member) ||
|
||||
ts.isGetAccessorDeclaration(member) ||
|
||||
ts.isSetAccessorDeclaration(member)) {
|
||||
var prop = member;
|
||||
var mods = ts.getCombinedModifierFlags(prop);
|
||||
var nonPrivate = (mods & ts.ModifierFlags.Private) === 0;
|
||||
var memSymbol = checker.getSymbolAtLocation(prop.name);
|
||||
if (nonPrivate && memSymbol && prop.decorators) {
|
||||
var name_1 = memSymbol.getName();
|
||||
var initializer = "";
|
||||
if (prop.initializer) {
|
||||
initializer = prop.initializer.getText(sourceFile);
|
||||
}
|
||||
var doc = ts.displayPartsToString(memSymbol.getDocumentationComment(checker));
|
||||
doc = doc.replace(/\r\n/g, " ");
|
||||
var propType = checker.typeToString(checker.getTypeOfSymbolAtLocation(memSymbol, memSymbol.valueDeclaration));
|
||||
var dec = prop.decorators[0].getText(sourceFile);
|
||||
if (dec.match(/@Input/)) {
|
||||
this.inputs.push({
|
||||
"name": name_1,
|
||||
"type": propType,
|
||||
"initializer": initializer,
|
||||
"docText": doc
|
||||
});
|
||||
}
|
||||
else if (dec.match(/@Output/)) {
|
||||
this.outputs.push({
|
||||
"name": name_1,
|
||||
"type": propType,
|
||||
"initializer": "",
|
||||
"docText": doc
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
ComponentDocAutoContent.prototype.addContentToDoc = function (tree) {
|
||||
var inTable = buildPropsTable(this.inputs);
|
||||
var outTable = buildPropsTable(this.outputs, false);
|
||||
if (inTable) {
|
||||
heading(tree, "Properties", function (before, section, after) {
|
||||
return [before, inTable, after];
|
||||
});
|
||||
}
|
||||
if (outTable) {
|
||||
heading(tree, "Events", function (before, section, after) {
|
||||
return [before, outTable, after];
|
||||
});
|
||||
}
|
||||
};
|
||||
return ComponentDocAutoContent;
|
||||
}());
|
||||
var ServiceDocAutoContent = /** @class */ (function () {
|
||||
function ServiceDocAutoContent() {
|
||||
this.props = [];
|
||||
this.methods = [];
|
||||
}
|
||||
ServiceDocAutoContent.prototype.extractClassInfoFromSource = function (checker, classDec) {
|
||||
var sourceFile = classDec.getSourceFile();
|
||||
for (var i = 0; i < classDec.members.length; i++) {
|
||||
var member = classDec.members[i];
|
||||
if (ts.isMethodDeclaration(member)) {
|
||||
var method = member;
|
||||
var mods = ts.getCombinedModifierFlags(method);
|
||||
var nonPrivate = (mods & ts.ModifierFlags.Private) === 0;
|
||||
var memSymbol = checker.getSymbolAtLocation(method.name);
|
||||
if (nonPrivate && memSymbol) {
|
||||
var methData = new MethodData();
|
||||
methData.name = memSymbol.getName();
|
||||
var doc = ts.displayPartsToString(memSymbol.getDocumentationComment());
|
||||
if (!doc)
|
||||
console.log("Warning: Method " + classDec.name.escapedText + "." + methData.name + " is not documented");
|
||||
methData.docText = doc.replace(/\r\n/g, " ");
|
||||
var sig = checker.getSignatureFromDeclaration(method);
|
||||
var returnType = sig.getReturnType();
|
||||
methData.returnType = checker.typeToString(returnType);
|
||||
var returnSymbol = returnType.getSymbol();
|
||||
var params = method.parameters;
|
||||
for (var p = 0; p < params.length; p++) {
|
||||
var pData = new ParamData();
|
||||
pData.name = params[p].name.getText();
|
||||
if (params[p].type)
|
||||
pData.type = params[p].type.getText();
|
||||
else
|
||||
pData.type = "";
|
||||
var paramSymbol = checker.getSymbolAtLocation(params[p].name);
|
||||
pData.docText = ts.displayPartsToString(paramSymbol.getDocumentationComment());
|
||||
if (!pData.docText)
|
||||
console.log("Warning: Parameter \"" + pData.name + "\" of " + classDec.name.escapedText + "." + methData.name + " is not documented");
|
||||
pData.optional = params[p].questionToken ? true : false;
|
||||
if (params[p].initializer) {
|
||||
var initText = params[p].initializer.getText();
|
||||
if (initText !== "undefined")
|
||||
pData.initializer = initText;
|
||||
}
|
||||
methData.params.push(pData);
|
||||
}
|
||||
this.methods.push(methData);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
ServiceDocAutoContent.prototype.addContentToDoc = function (tree) {
|
||||
var propsTable = buildPropsTable(this.props);
|
||||
var methodsList = buildMethodsList(this.methods);
|
||||
if (propsTable) {
|
||||
heading(tree, "Properties", function (before, section, after) {
|
||||
return [before, propsTable, after];
|
||||
});
|
||||
}
|
||||
if (methodsList) {
|
||||
heading(tree, "Methods", function (before, section, after) {
|
||||
return [before, methodsList, after];
|
||||
});
|
||||
}
|
||||
};
|
||||
return ServiceDocAutoContent;
|
||||
}());
|
||||
function updatePhase(tree, pathname, aggData) {
|
||||
var fileNameNoSuffix = path.basename(pathname, ".md");
|
||||
var itemType = fileNameNoSuffix.match(/component|directive|service/);
|
||||
if (itemType) {
|
||||
var srcData = aggData.srcData[fileNameNoSuffix];
|
||||
if (srcData) {
|
||||
var srcPath = srcData.path;
|
||||
var className = fixAngularFilename(fileNameNoSuffix);
|
||||
var classData = void 0;
|
||||
if ((itemType[0] === "component") || (itemType[0] === "directive")) {
|
||||
classData = new ComponentDocAutoContent();
|
||||
}
|
||||
else if (itemType[0] === "service") {
|
||||
classData = new ServiceDocAutoContent();
|
||||
}
|
||||
getDocSourceData(path.resolve(".", srcPath), className, classData);
|
||||
classData.addContentToDoc(tree);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
exports.updatePhase = updatePhase;
|
||||
function initialCap(str) {
|
||||
return str[0].toUpperCase() + str.substr(1);
|
||||
}
|
||||
function fixAngularFilename(rawName) {
|
||||
if (nameExceptions[rawName])
|
||||
return nameExceptions[rawName];
|
||||
var name = rawName.replace(/\]|\(|\)/g, '');
|
||||
var fileNameSections = name.split('.');
|
||||
var compNameSections = fileNameSections[0].split('-');
|
||||
var outCompName = '';
|
||||
for (var i = 0; i < compNameSections.length; i++) {
|
||||
outCompName = outCompName + initialCap(compNameSections[i]);
|
||||
}
|
||||
var itemTypeIndicator = '';
|
||||
if (fileNameSections.length > 1) {
|
||||
itemTypeIndicator = initialCap(fileNameSections[1]);
|
||||
}
|
||||
var finalName = outCompName + itemTypeIndicator;
|
||||
return finalName;
|
||||
}
|
||||
function getDocSourceData(srcPath, docClassName, classData) {
|
||||
var prog = ts.createProgram([srcPath], {
|
||||
target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
|
||||
});
|
||||
var sourceFiles = prog.getSourceFiles();
|
||||
var checker = prog.getTypeChecker();
|
||||
for (var i = 0; i < sourceFiles.length; i++) {
|
||||
if (!sourceFiles[i].isDeclarationFile)
|
||||
ts.forEachChild(sourceFiles[i], visit);
|
||||
}
|
||||
function visit(node) {
|
||||
if (!isNodeExported(node))
|
||||
return;
|
||||
if (ts.isClassDeclaration(node) && node.name) {
|
||||
var classDec = node;
|
||||
var sourceFile = classDec.getSourceFile();
|
||||
if (classDec.name.escapedText === docClassName) {
|
||||
getPropDataFromClassChain(checker, classDec, classData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get properties/events from main class and all inherited classes.
|
||||
function getPropDataFromClassChain(checker, classDec, classData) {
|
||||
// Main class
|
||||
classData.extractClassInfoFromSource(checker, classDec);
|
||||
// Inherited classes
|
||||
if (classDec.heritageClauses) {
|
||||
for (var _i = 0, _a = classDec.heritageClauses; _i < _a.length; _i++) {
|
||||
var hc = _a[_i];
|
||||
var hcType = checker.getTypeFromTypeNode(hc.types[0]);
|
||||
for (var _b = 0, _c = hcType.symbol.declarations; _b < _c.length; _b++) {
|
||||
var dec = _c[_b];
|
||||
if (typescript_1.isClassDeclaration(dec)) {
|
||||
getPropDataFromClassChain(checker, dec, classData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function buildPropsTable(props, includeInitializer) {
|
||||
if (includeInitializer === void 0) { includeInitializer = true; }
|
||||
if (props.length === 0) {
|
||||
return null;
|
||||
}
|
||||
var headerCells = [
|
||||
unist.makeTableCell([unist.makeText("Name")]),
|
||||
unist.makeTableCell([unist.makeText("Type")])
|
||||
];
|
||||
if (includeInitializer)
|
||||
headerCells.push(unist.makeTableCell([unist.makeText("Default value")]));
|
||||
headerCells.push(unist.makeTableCell([unist.makeText("Description")]));
|
||||
var rows = [
|
||||
unist.makeTableRow(headerCells)
|
||||
];
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var pName = props[i].name;
|
||||
var pType = props[i].type;
|
||||
var pDesc = props[i].docText || "";
|
||||
if (pDesc) {
|
||||
pDesc = pDesc.replace(/[\n\r]+/, " ");
|
||||
}
|
||||
var descCellContent = remark().parse(pDesc).children;
|
||||
var pDefault = props[i].initializer || "";
|
||||
var defaultCellContent;
|
||||
if (pDefault) {
|
||||
if (pDefault.length > maxDefaultTextLength) {
|
||||
defaultCellContent = unist.makeText("See description");
|
||||
console.log("Warning: property \"" + pName + "\" default value substituted (> " + maxDefaultTextLength + " chars)");
|
||||
}
|
||||
else
|
||||
defaultCellContent = unist.makeInlineCode(pDefault);
|
||||
}
|
||||
else {
|
||||
defaultCellContent = unist.makeText("");
|
||||
}
|
||||
var cells = [
|
||||
unist.makeTableCell([unist.makeText(pName)]),
|
||||
unist.makeTableCell([unist.makeInlineCode(pType)])
|
||||
];
|
||||
if (includeInitializer)
|
||||
cells.push(unist.makeTableCell([defaultCellContent]));
|
||||
cells.push(unist.makeTableCell(descCellContent));
|
||||
rows.push(unist.makeTableRow(cells));
|
||||
}
|
||||
var spacers = [null, null, null];
|
||||
if (includeInitializer)
|
||||
spacers.push(null);
|
||||
return unist.makeTable(spacers, rows);
|
||||
}
|
||||
function buildMethodsList(methods) {
|
||||
if (methods.length === 0)
|
||||
return null;
|
||||
var listItems = [];
|
||||
for (var _i = 0, methods_1 = methods; _i < methods_1.length; _i++) {
|
||||
var method = methods_1[_i];
|
||||
var itemLines = [];
|
||||
itemLines.push(unist.makeInlineCode(method.getSignature()));
|
||||
itemLines.push(unist.makeBreak());
|
||||
itemLines.push(unist.makeParagraph(remark().parse(method.docText).children));
|
||||
itemLines.push(unist.makeBreak());
|
||||
var paramListItems = [];
|
||||
for (var _a = 0, _b = method.params; _a < _b.length; _a++) {
|
||||
var param = _b[_a];
|
||||
var currParamSections = [];
|
||||
if (param.docText !== "") {
|
||||
currParamSections.push(unist.makeInlineCode(param.name));
|
||||
var optionalPart = param.optional ? "(Optional) " : "";
|
||||
currParamSections.push(unist.makeText(" - " + optionalPart + param.docText));
|
||||
//currParamSections.push(unist.makeBreak());
|
||||
paramListItems.push(unist.makeListItem(unist.makeParagraph(currParamSections)));
|
||||
}
|
||||
}
|
||||
itemLines.push(unist.makeListUnordered(paramListItems));
|
||||
listItems.push(unist.makeListItem(unist.makeParagraph(itemLines)));
|
||||
}
|
||||
return unist.makeListUnordered(listItems);
|
||||
}
|
||||
function isNodeExported(node) {
|
||||
return (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) !== 0 || (!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile);
|
||||
}
|
||||
@@ -1,498 +0,0 @@
|
||||
import * as ts from "typescript";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import * as heading from "mdast-util-heading-range";
|
||||
import * as remark from "remark";
|
||||
|
||||
import * as unist from "../unistHelpers";
|
||||
import { JsxEmit, isClassDeclaration, PropertyDeclaration } from "typescript";
|
||||
|
||||
|
||||
// Max number of characters in the text for the default value column.
|
||||
const maxDefaultTextLength = 20;
|
||||
|
||||
let nameExceptions = {
|
||||
"datatable.component": "DataTableComponent",
|
||||
"tasklist.service": "TaskListService"
|
||||
}
|
||||
|
||||
|
||||
export function initPhase(aggData) {
|
||||
}
|
||||
|
||||
export function readPhase(tree, pathname, aggData) {
|
||||
}
|
||||
|
||||
export function aggPhase(aggData) {
|
||||
}
|
||||
|
||||
|
||||
interface NgDocAutoContent {
|
||||
extractClassInfoFromSource(checker: ts.TypeChecker, classDec: ts.ClassDeclaration);
|
||||
addContentToDoc(tree);
|
||||
}
|
||||
|
||||
class PropData {
|
||||
name: string;
|
||||
type: string;
|
||||
initializer: string;
|
||||
docText: string;
|
||||
}
|
||||
|
||||
class ParamData {
|
||||
name: string;
|
||||
type: string;
|
||||
docText: string;
|
||||
initializer: string;
|
||||
optional: boolean;
|
||||
|
||||
getSignature() {
|
||||
let sig = this.name;
|
||||
|
||||
if (this.optional)
|
||||
sig += "?";
|
||||
|
||||
if (this.type)
|
||||
sig += ": " + this.type;
|
||||
|
||||
if (this.initializer)
|
||||
sig += " = " + this.initializer;
|
||||
|
||||
return sig;
|
||||
}
|
||||
}
|
||||
|
||||
class MethodData {
|
||||
name: string;
|
||||
docText: string;
|
||||
params: ParamData[];
|
||||
returnType: string;
|
||||
|
||||
constructor() {
|
||||
this.params = [];
|
||||
}
|
||||
|
||||
getSignature() {
|
||||
let sig = this.name + "(";
|
||||
|
||||
if (this.params.length > 0) {
|
||||
sig += this.params[0].getSignature();
|
||||
}
|
||||
|
||||
for (let i = 1; i < this.params.length; i++) {
|
||||
sig += ", " + this.params[i].getSignature();
|
||||
}
|
||||
|
||||
sig += ")";
|
||||
|
||||
if (this.returnType !== "void") {
|
||||
sig += ": " + this.returnType;
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
}
|
||||
|
||||
class ComponentDocAutoContent implements NgDocAutoContent {
|
||||
inputs: PropData[];
|
||||
outputs: PropData[];
|
||||
|
||||
constructor() {
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
}
|
||||
|
||||
|
||||
extractClassInfoFromSource(checker: ts.TypeChecker, classDec: ts.ClassDeclaration) {
|
||||
let sourceFile = classDec.getSourceFile();
|
||||
|
||||
for (var i = 0; i < classDec.members.length; i++) {
|
||||
let member = classDec.members[i];
|
||||
|
||||
if (ts.isPropertyDeclaration(member) ||
|
||||
ts.isGetAccessorDeclaration(member) ||
|
||||
ts.isSetAccessorDeclaration(member)) {
|
||||
let prop: ts.PropertyDeclaration = member;
|
||||
|
||||
let mods = ts.getCombinedModifierFlags(prop);
|
||||
let nonPrivate = (mods & ts.ModifierFlags.Private) === 0;
|
||||
let memSymbol = checker.getSymbolAtLocation(prop.name);
|
||||
|
||||
if (nonPrivate && memSymbol && prop.decorators) {
|
||||
let name = memSymbol.getName();
|
||||
let initializer = "";
|
||||
|
||||
if (prop.initializer) {
|
||||
initializer = prop.initializer.getText(sourceFile);
|
||||
}
|
||||
|
||||
let doc = ts.displayPartsToString(memSymbol.getDocumentationComment(checker));
|
||||
doc = doc.replace(/\r\n/g, " ");
|
||||
|
||||
let propType = checker.typeToString(checker.getTypeOfSymbolAtLocation(memSymbol, memSymbol.valueDeclaration!));
|
||||
|
||||
let dec = prop.decorators[0].getText(sourceFile);
|
||||
|
||||
if (dec.match(/@Input/)) {
|
||||
this.inputs.push({
|
||||
"name": name,
|
||||
"type": propType,
|
||||
"initializer": initializer,
|
||||
"docText": doc
|
||||
});
|
||||
} else if (dec.match(/@Output/)) {
|
||||
this.outputs.push({
|
||||
"name": name,
|
||||
"type": propType,
|
||||
"initializer": "",
|
||||
"docText": doc
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
addContentToDoc(tree) {
|
||||
let inTable = buildPropsTable(this.inputs);
|
||||
let outTable = buildPropsTable(this.outputs, false);
|
||||
|
||||
if (inTable) {
|
||||
heading(tree, "Properties", (before, section, after) => {
|
||||
return [before, inTable, after];
|
||||
});
|
||||
}
|
||||
|
||||
if (outTable) {
|
||||
heading(tree, "Events", (before, section, after) => {
|
||||
return [before, outTable, after];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ServiceDocAutoContent implements NgDocAutoContent {
|
||||
props: PropData[];
|
||||
methods: MethodData[];
|
||||
|
||||
constructor() {
|
||||
this.props = [];
|
||||
this.methods = [];
|
||||
}
|
||||
|
||||
|
||||
extractClassInfoFromSource(checker: ts.TypeChecker, classDec: ts.ClassDeclaration) {
|
||||
let sourceFile = classDec.getSourceFile();
|
||||
|
||||
for (var i = 0; i < classDec.members.length; i++) {
|
||||
let member = classDec.members[i];
|
||||
|
||||
if (ts.isMethodDeclaration(member)) {
|
||||
let method: ts.MethodDeclaration = member;
|
||||
|
||||
let mods = ts.getCombinedModifierFlags(method);
|
||||
let nonPrivate = (mods & ts.ModifierFlags.Private) === 0;
|
||||
let memSymbol = checker.getSymbolAtLocation(method.name);
|
||||
|
||||
if (nonPrivate && memSymbol) {
|
||||
let methData = new MethodData();
|
||||
|
||||
methData.name = memSymbol.getName();
|
||||
let doc = ts.displayPartsToString(memSymbol.getDocumentationComment());
|
||||
|
||||
if (!doc)
|
||||
console.log(`Warning: Method ${classDec.name.escapedText}.${methData.name} is not documented`);
|
||||
|
||||
methData.docText = doc.replace(/\r\n/g, " ");
|
||||
let sig = checker.getSignatureFromDeclaration(method);
|
||||
let returnType = sig.getReturnType();
|
||||
methData.returnType = checker.typeToString(returnType);
|
||||
let returnSymbol = returnType.getSymbol();
|
||||
|
||||
let params = method.parameters;
|
||||
|
||||
for (let p = 0; p < params.length; p++){
|
||||
let pData = new ParamData();
|
||||
pData.name = params[p].name.getText();
|
||||
|
||||
if (params[p].type)
|
||||
pData.type = params[p].type.getText();
|
||||
else
|
||||
pData.type = "";
|
||||
|
||||
let paramSymbol = checker.getSymbolAtLocation(params[p].name);
|
||||
pData.docText = ts.displayPartsToString(paramSymbol.getDocumentationComment());
|
||||
|
||||
if (!pData.docText)
|
||||
console.log(`Warning: Parameter "${pData.name}" of ${classDec.name.escapedText}.${methData.name} is not documented`);
|
||||
|
||||
pData.optional = params[p].questionToken ? true : false;
|
||||
|
||||
if (params[p].initializer) {
|
||||
let initText = params[p].initializer.getText();
|
||||
|
||||
if (initText !== "undefined")
|
||||
pData.initializer = initText;
|
||||
}
|
||||
|
||||
methData.params.push(pData);
|
||||
}
|
||||
|
||||
this.methods.push(methData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
addContentToDoc(tree) {
|
||||
let propsTable = buildPropsTable(this.props);
|
||||
let methodsList = buildMethodsList(this.methods);
|
||||
|
||||
if (propsTable) {
|
||||
heading(tree, "Properties", (before, section, after) => {
|
||||
return [before, propsTable, after];
|
||||
});
|
||||
}
|
||||
|
||||
if (methodsList) {
|
||||
heading(tree, "Methods", (before, section, after) => {
|
||||
return [before, methodsList, after];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function updatePhase(tree, pathname, aggData) {
|
||||
let fileNameNoSuffix = path.basename(pathname, ".md");
|
||||
|
||||
let itemType = fileNameNoSuffix.match(/component|directive|service/);
|
||||
|
||||
if (itemType) {
|
||||
let srcData = aggData.srcData[fileNameNoSuffix];
|
||||
|
||||
if (srcData) {
|
||||
let srcPath = srcData.path;
|
||||
let className = fixAngularFilename(fileNameNoSuffix);
|
||||
|
||||
let classData: NgDocAutoContent;
|
||||
|
||||
if ((itemType[0] === "component") || (itemType[0] === "directive")) {
|
||||
classData = new ComponentDocAutoContent();
|
||||
} else if (itemType[0] === "service") {
|
||||
classData = new ServiceDocAutoContent();
|
||||
}
|
||||
|
||||
getDocSourceData(path.resolve(".", srcPath), className, classData);
|
||||
classData.addContentToDoc(tree);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function initialCap(str: string) {
|
||||
return str[0].toUpperCase() + str.substr(1);
|
||||
}
|
||||
|
||||
|
||||
function fixAngularFilename(rawName: string) {
|
||||
if (nameExceptions[rawName])
|
||||
return nameExceptions[rawName];
|
||||
|
||||
var name = rawName.replace(/\]|\(|\)/g, '');
|
||||
|
||||
var fileNameSections = name.split('.');
|
||||
var compNameSections = fileNameSections[0].split('-');
|
||||
|
||||
var outCompName = '';
|
||||
|
||||
for (var i = 0; i < compNameSections.length; i++) {
|
||||
outCompName = outCompName + initialCap(compNameSections[i]);
|
||||
}
|
||||
|
||||
var itemTypeIndicator = '';
|
||||
|
||||
if (fileNameSections.length > 1) {
|
||||
itemTypeIndicator = initialCap(fileNameSections[1]);
|
||||
}
|
||||
|
||||
var finalName = outCompName + itemTypeIndicator;
|
||||
|
||||
return finalName;
|
||||
}
|
||||
|
||||
|
||||
function getDocSourceData(srcPath: string, docClassName: string, classData: NgDocAutoContent) {
|
||||
let prog = ts.createProgram([srcPath], {
|
||||
target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
|
||||
});
|
||||
|
||||
let sourceFiles = prog.getSourceFiles();
|
||||
let checker = prog.getTypeChecker();
|
||||
|
||||
for (var i = 0; i < sourceFiles.length; i++) {
|
||||
if (!sourceFiles[i].isDeclarationFile)
|
||||
ts.forEachChild(sourceFiles[i], visit);
|
||||
}
|
||||
|
||||
function visit(node: ts.Node) {
|
||||
if (!isNodeExported(node))
|
||||
return;
|
||||
|
||||
if (ts.isClassDeclaration(node) && node.name) {
|
||||
let classDec: ts.ClassDeclaration = node;
|
||||
let sourceFile = classDec.getSourceFile();
|
||||
|
||||
if (classDec.name.escapedText === docClassName) {
|
||||
getPropDataFromClassChain(checker, classDec, classData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get properties/events from main class and all inherited classes.
|
||||
function getPropDataFromClassChain(
|
||||
checker: ts.TypeChecker,
|
||||
classDec: ts.ClassDeclaration,
|
||||
classData: NgDocAutoContent
|
||||
){
|
||||
// Main class
|
||||
classData.extractClassInfoFromSource(checker, classDec);
|
||||
|
||||
// Inherited classes
|
||||
if (classDec.heritageClauses) {
|
||||
for(const hc of classDec.heritageClauses) {
|
||||
let hcType = checker.getTypeFromTypeNode(hc.types[0]);
|
||||
|
||||
for (const dec of hcType.symbol.declarations) {
|
||||
if (isClassDeclaration(dec)) {
|
||||
getPropDataFromClassChain(checker, dec, classData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function buildPropsTable(props: PropData[], includeInitializer: boolean = true) {
|
||||
if (props.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var headerCells = [
|
||||
unist.makeTableCell([unist.makeText("Name")]),
|
||||
unist.makeTableCell([unist.makeText("Type")])
|
||||
];
|
||||
|
||||
if (includeInitializer)
|
||||
headerCells.push(unist.makeTableCell([unist.makeText("Default value")]));
|
||||
|
||||
headerCells.push(unist.makeTableCell([unist.makeText("Description")]));
|
||||
|
||||
var rows = [
|
||||
unist.makeTableRow(headerCells)
|
||||
];
|
||||
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var pName = props[i].name;
|
||||
var pType = props[i].type;
|
||||
var pDesc = props[i].docText || "";
|
||||
|
||||
if (pDesc) {
|
||||
pDesc = pDesc.replace(/[\n\r]+/, " ");
|
||||
}
|
||||
|
||||
var descCellContent = remark().parse(pDesc).children;
|
||||
|
||||
var pDefault = props[i].initializer || "";
|
||||
|
||||
var defaultCellContent;
|
||||
|
||||
if (pDefault) {
|
||||
if (pDefault.length > maxDefaultTextLength) {
|
||||
defaultCellContent = unist.makeText("See description");
|
||||
console.log(`Warning: property "${pName}" default value substituted (> ${maxDefaultTextLength} chars)`);
|
||||
} else
|
||||
defaultCellContent = unist.makeInlineCode(pDefault);
|
||||
} else {
|
||||
defaultCellContent = unist.makeText("");
|
||||
}
|
||||
|
||||
var cells = [
|
||||
unist.makeTableCell([unist.makeText(pName)]),
|
||||
unist.makeTableCell([unist.makeInlineCode(pType)])
|
||||
];
|
||||
|
||||
if (includeInitializer)
|
||||
cells.push(unist.makeTableCell([defaultCellContent]));
|
||||
|
||||
cells.push(unist.makeTableCell(descCellContent));
|
||||
|
||||
rows.push(unist.makeTableRow(cells));
|
||||
}
|
||||
|
||||
let spacers = [null, null, null];
|
||||
|
||||
if (includeInitializer)
|
||||
spacers.push(null);
|
||||
|
||||
return unist.makeTable(spacers, rows);
|
||||
}
|
||||
|
||||
|
||||
function buildMethodsList(methods: MethodData[]) {
|
||||
if (methods.length === 0)
|
||||
return null;
|
||||
|
||||
let listItems = [];
|
||||
|
||||
for (let method of methods) {
|
||||
let itemLines = [];
|
||||
|
||||
itemLines.push(unist.makeInlineCode(method.getSignature()));
|
||||
itemLines.push(unist.makeBreak());
|
||||
itemLines.push(unist.makeParagraph(remark().parse(method.docText).children));
|
||||
itemLines.push(unist.makeBreak());
|
||||
|
||||
let paramListItems = [];
|
||||
|
||||
for (let param of method.params) {
|
||||
let currParamSections = [];
|
||||
|
||||
if (param.docText !== "") {
|
||||
currParamSections.push(unist.makeInlineCode(param.name));
|
||||
|
||||
let optionalPart = param.optional ? "(Optional) " : "";
|
||||
|
||||
currParamSections.push(unist.makeText(" - " + optionalPart + param.docText));
|
||||
//currParamSections.push(unist.makeBreak());
|
||||
paramListItems.push(unist.makeListItem(unist.makeParagraph(currParamSections)));
|
||||
}
|
||||
}
|
||||
|
||||
itemLines.push(unist.makeListUnordered(paramListItems));
|
||||
|
||||
|
||||
listItems.push(unist.makeListItem(unist.makeParagraph(itemLines)));
|
||||
}
|
||||
|
||||
return unist.makeListUnordered(listItems);
|
||||
}
|
||||
|
||||
|
||||
function isNodeExported(node: ts.Node): boolean {
|
||||
return (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) !== 0 || (!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile);
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var replaceSection = require("mdast-util-heading-range");
|
||||
var remark = require("remark");
|
||||
var frontMatter = require("remark-frontmatter");
|
||||
var yaml = require("js-yaml");
|
||||
var combyne = require("combyne");
|
||||
var unist = require("../unistHelpers");
|
||||
var tutFolder = path.resolve("..", "docs", "tutorials");
|
||||
var templateFolder = path.resolve(".", "config", "DocProcessor", "templates");
|
||||
var userGuideFolder = path.resolve("..", "docs", "user-guide");
|
||||
function initPhase(aggData) { }
|
||||
exports.initPhase = initPhase;
|
||||
function readPhase(tree, pathname, aggData) { }
|
||||
exports.readPhase = readPhase;
|
||||
function aggPhase(aggData) {
|
||||
var indexDocData = getIndexDocData();
|
||||
var templateName = path.resolve(templateFolder, "tutIndex.combyne");
|
||||
var templateSource = fs.readFileSync(templateName, "utf8");
|
||||
var template = combyne(templateSource);
|
||||
var mdText = template.render(indexDocData);
|
||||
mdText = mdText.replace(/^ +\|/mg, "|");
|
||||
var newSection = remark().data("settings", { paddedTable: false, gfm: false }).parse(mdText.trim()).children;
|
||||
var tutIndexFile = path.resolve(tutFolder, "README.md");
|
||||
var tutIndexText = fs.readFileSync(tutIndexFile, "utf8");
|
||||
var tutIndexMD = remark().data("settings", { paddedTable: false, gfm: false }).parse(tutIndexText);
|
||||
replaceSection(tutIndexMD, "Tutorials", function (before, section, after) {
|
||||
newSection.unshift(before);
|
||||
newSection.push(after);
|
||||
return newSection;
|
||||
});
|
||||
fs.writeFileSync(tutIndexFile, remark().use(frontMatter, { type: 'yaml', fence: '---' }).data("settings", { paddedTable: false, gfm: false }).stringify(tutIndexMD));
|
||||
}
|
||||
exports.aggPhase = aggPhase;
|
||||
function updatePhase(tree, pathname, aggData) {
|
||||
return false;
|
||||
}
|
||||
exports.updatePhase = updatePhase;
|
||||
function getIndexDocData() {
|
||||
var indexFile = path.resolve(userGuideFolder, "summary.json");
|
||||
var summaryArray = JSON.parse(fs.readFileSync(indexFile, "utf8"));
|
||||
var indexArray = [];
|
||||
summaryArray.forEach(function (element) {
|
||||
if (element["title"] === "Tutorials") {
|
||||
indexArray = element["children"];
|
||||
}
|
||||
});
|
||||
var result = {
|
||||
tuts: []
|
||||
};
|
||||
indexArray.forEach(function (element) {
|
||||
var tutData = { link: element["file"] };
|
||||
var tutFile = path.resolve(tutFolder, element["file"]);
|
||||
var tutFileText = fs.readFileSync(tutFile, "utf8");
|
||||
var tutMD = remark().use(frontMatter, ["yaml"]).parse(tutFileText);
|
||||
var metadata = getDocMetadata(tutMD);
|
||||
if (metadata["Level"]) {
|
||||
tutData["level"] = metadata["Level"];
|
||||
}
|
||||
else {
|
||||
tutData["level"] = "";
|
||||
}
|
||||
var briefDesc = getFirstParagraph(tutMD);
|
||||
var briefDescText = remark()
|
||||
.use(frontMatter, { type: 'yaml', fence: '---' })
|
||||
.data("settings", { paddedTable: false, gfm: false })
|
||||
.stringify(briefDesc);
|
||||
tutData["briefDesc"] = briefDescText;
|
||||
var title = getFirstHeading(tutMD);
|
||||
var titleText = remark()
|
||||
.use(frontMatter, { type: 'yaml', fence: '---' })
|
||||
.data("settings", { paddedTable: false, gfm: false })
|
||||
.stringify(title.children[0]);
|
||||
tutData["title"] = titleText;
|
||||
result.tuts.push(tutData);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
function getDocMetadata(tree) {
|
||||
if (tree.children[0].type == "yaml") {
|
||||
return yaml.load(tree.children[0].value);
|
||||
}
|
||||
else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
function getFirstParagraph(tree) {
|
||||
var s = 0;
|
||||
for (; (s < tree.children.length) && !unist.isParagraph(tree.children[s]); s++) { }
|
||||
if (s < tree.children.length) {
|
||||
return tree.children[s];
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function getFirstHeading(tree) {
|
||||
var s = 0;
|
||||
for (; (s < tree.children.length) && !unist.isHeading(tree.children[s]); s++) { }
|
||||
if (s < tree.children.length) {
|
||||
return tree.children[s];
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import * as replaceSection from "mdast-util-heading-range";
|
||||
import * as remark from "remark";
|
||||
import * as stringify from "remark-stringify";
|
||||
import * as frontMatter from "remark-frontmatter";
|
||||
import * as yaml from "js-yaml";
|
||||
import * as combyne from "combyne";
|
||||
|
||||
import * as unist from "../unistHelpers";
|
||||
|
||||
const tutFolder = path.resolve("..", "docs", "tutorials");
|
||||
const templateFolder = path.resolve(".", "config", "DocProcessor", "templates");
|
||||
const userGuideFolder = path.resolve("..", "docs", "user-guide");
|
||||
|
||||
|
||||
export function initPhase(aggData) {}
|
||||
|
||||
export function readPhase(tree, pathname, aggData) {}
|
||||
|
||||
|
||||
export function aggPhase(aggData) {
|
||||
let indexDocData = getIndexDocData();
|
||||
|
||||
let templateName = path.resolve(templateFolder, "tutIndex.combyne");
|
||||
let templateSource = fs.readFileSync(templateName, "utf8");
|
||||
let template = combyne(templateSource);
|
||||
|
||||
let mdText = template.render(indexDocData);
|
||||
mdText = mdText.replace(/^ +\|/mg, "|");
|
||||
|
||||
let newSection = remark().data("settings", {paddedTable: false, gfm: false}).parse(mdText.trim()).children;
|
||||
|
||||
let tutIndexFile = path.resolve(tutFolder, "README.md");
|
||||
let tutIndexText = fs.readFileSync(tutIndexFile, "utf8");
|
||||
let tutIndexMD = remark().data("settings", {paddedTable: false, gfm: false}).parse(tutIndexText);
|
||||
|
||||
replaceSection(tutIndexMD, "Tutorials", (before, section, after) => {
|
||||
newSection.unshift(before);
|
||||
newSection.push(after);
|
||||
return newSection;
|
||||
});
|
||||
|
||||
fs.writeFileSync(tutIndexFile, remark().use(frontMatter, {type: 'yaml', fence: '---'}).data("settings", {paddedTable: false, gfm: false}).stringify(tutIndexMD));
|
||||
}
|
||||
|
||||
|
||||
export function updatePhase(tree, pathname, aggData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function getIndexDocData() {
|
||||
let indexFile = path.resolve(userGuideFolder, "summary.json");
|
||||
let summaryArray = JSON.parse(fs.readFileSync(indexFile, "utf8"));
|
||||
let indexArray = [];
|
||||
|
||||
summaryArray.forEach(element => {
|
||||
if (element["title"] === "Tutorials") {
|
||||
indexArray = element["children"];
|
||||
}
|
||||
});
|
||||
|
||||
let result = {
|
||||
tuts: []
|
||||
};
|
||||
|
||||
indexArray.forEach(element => {
|
||||
let tutData = { link: element["file"] };
|
||||
|
||||
let tutFile = path.resolve(tutFolder, element["file"]);
|
||||
let tutFileText = fs.readFileSync(tutFile, "utf8");
|
||||
let tutMD = remark().use(frontMatter, ["yaml"]).parse(tutFileText);
|
||||
|
||||
let metadata = getDocMetadata(tutMD);
|
||||
|
||||
if (metadata["Level"]){
|
||||
tutData["level"] = metadata["Level"];
|
||||
} else {
|
||||
tutData["level"] = "";
|
||||
}
|
||||
|
||||
let briefDesc = getFirstParagraph(tutMD);
|
||||
|
||||
let briefDescText = remark()
|
||||
.use(frontMatter, {type: 'yaml', fence: '---'})
|
||||
.data("settings", {paddedTable: false, gfm: false})
|
||||
.stringify(briefDesc);
|
||||
|
||||
tutData["briefDesc"] = briefDescText;
|
||||
|
||||
let title = getFirstHeading(tutMD);
|
||||
|
||||
let titleText = remark()
|
||||
.use(frontMatter, {type: 'yaml', fence: '---'})
|
||||
.data("settings", {paddedTable: false, gfm: false})
|
||||
.stringify(title.children[0]);
|
||||
|
||||
tutData["title"] = titleText;
|
||||
|
||||
result.tuts.push(tutData);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function getDocMetadata(tree) {
|
||||
if (tree.children[0].type == "yaml") {
|
||||
return yaml.load(tree.children[0].value);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getFirstParagraph(tree) {
|
||||
let s = 0;
|
||||
|
||||
for (;(s < tree.children.length) && !unist.isParagraph(tree.children[s]); s++) {}
|
||||
|
||||
if (s < tree.children.length) {
|
||||
return tree.children[s];
|
||||
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getFirstHeading(tree) {
|
||||
let s = 0;
|
||||
|
||||
for (;(s < tree.children.length) && !unist.isHeading(tree.children[s]); s++) {}
|
||||
|
||||
if (s < tree.children.length) {
|
||||
return tree.children[s];
|
||||
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var yaml = require("js-yaml");
|
||||
|
||||
var remark = require("remark");
|
||||
var stringify = require("remark-stringify");
|
||||
var zone = require("mdast-zone");
|
||||
|
||||
var unist = require("../unistHelpers");
|
||||
var ngHelpers = require("../ngHelpers");
|
||||
|
||||
|
||||
module.exports = {
|
||||
"initPhase": initPhase,
|
||||
"readPhase": readPhase,
|
||||
"aggPhase": aggPhase,
|
||||
"updatePhase": updatePhase
|
||||
}
|
||||
|
||||
var angFilenameRegex = /([a-zA-Z0-9\-]+)\.((component)|(directive)|(model)|(pipe)|(service)|(widget))\.ts/;
|
||||
|
||||
var docsFolderPath = path.resolve("..", "docs");
|
||||
var histFilePath = path.resolve(docsFolderPath, "versionIndex.md");
|
||||
|
||||
var histSectionName = "history";
|
||||
var initialVersion = "v2.0.0";
|
||||
|
||||
function initPhase(aggData) {
|
||||
aggData.versions = { "v2.0.0":[] };
|
||||
}
|
||||
|
||||
|
||||
function readPhase(tree, pathname, aggData) {
|
||||
var compName = pathname; //path.basename(pathname, ".md");
|
||||
var angNameRegex = /([a-zA-Z0-9\-]+)\.((component)|(directive)|(model)|(pipe)|(service)|(widget))/;
|
||||
|
||||
if (!compName.match(angNameRegex))
|
||||
return;
|
||||
|
||||
if (compName.match(/boilerplate/))
|
||||
return;
|
||||
|
||||
if (tree.children[0].type == "yaml") {
|
||||
var metadata = yaml.load(tree.children[0].value);
|
||||
var version = metadata["Added"];
|
||||
|
||||
if (version) {
|
||||
if (aggData.versions[version]) {
|
||||
aggData.versions[version].push(compName);
|
||||
} else {
|
||||
aggData.versions[version] = [compName];
|
||||
}
|
||||
} else {
|
||||
aggData.versions[initialVersion].push(compName);
|
||||
}
|
||||
} else {
|
||||
aggData.versions[initialVersion].push(compName);
|
||||
}
|
||||
}
|
||||
|
||||
function aggPhase(aggData) {
|
||||
var histFileText = fs.readFileSync(histFilePath, "utf8");
|
||||
var histFileTree = remark().parse(histFileText);
|
||||
|
||||
var keys = Object.keys(aggData.versions);
|
||||
keys.sort((a, b) => {
|
||||
if (a > b)
|
||||
return -1;
|
||||
else if (b > a)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
});
|
||||
|
||||
var sections = [unist.makeHeading(unist.makeText("Versions"), 2)];
|
||||
|
||||
var vListItems = [];
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var keyAnchor = keys[i].replace(/\./g, "");
|
||||
vListItems.push(unist.makeListItem(unist.makeLink(unist.makeText(keys[i]), `#${keyAnchor}`)));
|
||||
}
|
||||
|
||||
sections.push(unist.makeListUnordered(vListItems));
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var version = keys[i];
|
||||
var versionItems = aggData.versions[version];
|
||||
versionItems.sort((a, b) => {
|
||||
var aa = path.basename(a, ".md");
|
||||
var bb = path.basename(b, ".md");
|
||||
|
||||
return aa.localeCompare(bb);
|
||||
});
|
||||
|
||||
var versListItems = [];
|
||||
|
||||
for (var v = 0; v < versionItems.length; v++) {
|
||||
var displayName = ngHelpers.ngNameToDisplayName(path.basename(versionItems[v], ".md"));
|
||||
var pageLink = versionItems[v];// + ".md";
|
||||
pageLink = pageLink.replace(/\\/g, '/');
|
||||
pageLink = pageLink.substr(pageLink.indexOf("docs") + 5);
|
||||
|
||||
versListItems.push(
|
||||
unist.makeListItem(
|
||||
unist.makeLink(unist.makeText(displayName), pageLink)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
sections.push(unist.makeHeading(unist.makeText(version), 2));
|
||||
sections.push(unist.makeListUnordered(versListItems));
|
||||
}
|
||||
|
||||
zone(histFileTree, histSectionName, (startComment, oldSection, endComment) => {
|
||||
sections.unshift(startComment);
|
||||
sections.push(endComment);
|
||||
return sections;
|
||||
});
|
||||
|
||||
//console.log(JSON.stringify(histFileTree));
|
||||
fs.writeFileSync(histFilePath, remark().stringify(histFileTree));
|
||||
}
|
||||
|
||||
|
||||
function updatePhase(tree, pathname, aggData) {
|
||||
return false;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
module.exports = {
|
||||
|
||||
makeRoot: function (children) {
|
||||
return {
|
||||
"type": "root",
|
||||
"children": children
|
||||
};
|
||||
},
|
||||
|
||||
makeText: function (textVal) {
|
||||
return {
|
||||
"type": "text",
|
||||
"value": textVal
|
||||
};
|
||||
},
|
||||
|
||||
makeEmphasis: function (content) {
|
||||
return {
|
||||
"type": "emphasis",
|
||||
"children": content
|
||||
};
|
||||
},
|
||||
|
||||
makeStrong: function (content) {
|
||||
return {
|
||||
"type": "strong",
|
||||
"children": content
|
||||
};
|
||||
},
|
||||
|
||||
makeHeading: function (caption, depth) {
|
||||
return {
|
||||
"type": "heading",
|
||||
"depth": depth,
|
||||
"children": [caption]
|
||||
};
|
||||
},
|
||||
|
||||
makeLink: function (caption, url) {
|
||||
return {
|
||||
"type": "link",
|
||||
"url": url,
|
||||
"children": [ caption ]
|
||||
};
|
||||
},
|
||||
|
||||
makeListItem: function (itemValue) {
|
||||
return {
|
||||
"type": "listItem",
|
||||
"loose": false,
|
||||
"children": [ itemValue ]
|
||||
};
|
||||
},
|
||||
|
||||
makeListUnordered: function (itemsArray) {
|
||||
return {
|
||||
"type": "list",
|
||||
"ordered": false,
|
||||
"children": itemsArray,
|
||||
"loose": false
|
||||
};
|
||||
},
|
||||
|
||||
makeParagraph: function (itemsArray) {
|
||||
return {
|
||||
"type": "paragraph",
|
||||
"children": itemsArray
|
||||
}
|
||||
},
|
||||
|
||||
makeTable: function (colAlignArray, rowArray) {
|
||||
return {
|
||||
"type": "table",
|
||||
"align": colAlignArray,
|
||||
"children": rowArray
|
||||
};
|
||||
},
|
||||
|
||||
makeTableRow: function (cellArray) {
|
||||
return {
|
||||
"type": "tableRow",
|
||||
"children": cellArray
|
||||
};
|
||||
},
|
||||
|
||||
makeTableCell: function (content) {
|
||||
return {
|
||||
"type": "tableCell",
|
||||
"children": content
|
||||
};
|
||||
},
|
||||
|
||||
makeInlineCode: function (codeText) {
|
||||
return {
|
||||
"type": "inlineCode",
|
||||
"value": codeText
|
||||
}
|
||||
},
|
||||
|
||||
makeHTML: function (htmlText) {
|
||||
return {
|
||||
"type": "html",
|
||||
"value": htmlText
|
||||
};
|
||||
},
|
||||
|
||||
makeBreak: function () {
|
||||
return {
|
||||
"type": "break"
|
||||
}
|
||||
},
|
||||
|
||||
makeImage: function (url, alt) {
|
||||
return {
|
||||
"type": "image",
|
||||
"url": url,
|
||||
"alt": alt
|
||||
}
|
||||
},
|
||||
|
||||
isHeading: function (node) {
|
||||
return node.type === "heading";
|
||||
},
|
||||
|
||||
isListUnordered: function (node) {
|
||||
return (node.type === "list") && !node.ordered;
|
||||
},
|
||||
|
||||
isParagraph: function (node) {
|
||||
return node.type === "paragraph";
|
||||
},
|
||||
|
||||
isText: function (node) {
|
||||
return node.type === "text";
|
||||
}
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
var angFilenameRegex = /([a-zA-Z0-9\-]+)\.((component)|(directive)|(model)|(pipe)|(service)|(widget))\.ts/;
|
||||
var searchFolderOmitRegex = /(mock)|(i18n)|(assets)|(styles)/;
|
||||
var libFolderOmitRegex = /config/;
|
||||
|
||||
var indexFileName = path.resolve('..', 'docs', 'README.md');
|
||||
var summaryFileName = path.resolve('..', 'docs', 'summary.json');
|
||||
var undocStoplistFileName = path.resolve('..', 'docs', 'undocStoplist.json');
|
||||
|
||||
|
||||
// Search source folders for .ts files to discover all components, directives, etc,
|
||||
// that are in the supplied library.
|
||||
function searchLibraryRecursive(libData, folderPath) {
|
||||
var items = fs.readdirSync(folderPath);
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var itemPath = path.resolve(folderPath, items[i]);
|
||||
var info = fs.statSync(itemPath);
|
||||
|
||||
if (info.isFile() && (items[i].match(angFilenameRegex))) {
|
||||
var nameNoSuffix = path.basename(items[i], '.ts');
|
||||
|
||||
var itemCategory = nameNoSuffix.split('.')[1];
|
||||
|
||||
if(nameNoSuffix in docDict) {
|
||||
switch (itemCategory) {
|
||||
case "component":
|
||||
libData.componentsWithDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "directive":
|
||||
libData.directivesWithDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "model":
|
||||
libData.modelsWithDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "pipe":
|
||||
libData.pipesWithDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "service":
|
||||
libData.servicesWithDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "widget":
|
||||
libData.widgetsWithDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (!rejectItemViaStoplist(undocStoplist, items[i])) {
|
||||
switch (itemCategory) {
|
||||
case "component":
|
||||
libData.componentsWithoutDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "directive":
|
||||
libData.directivesWithoutDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "model":
|
||||
libData.modelsWithoutDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "pipe":
|
||||
libData.pipesWithoutDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "service":
|
||||
libData.servicesWithoutDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
case "widget":
|
||||
libData.widgetsWithoutDocs.push(itemPath);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (info.isDirectory() && !items[i].match(searchFolderOmitRegex)) {
|
||||
searchLibraryRecursive(libData, itemPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get a list of all items that have a file in the docs folder.
|
||||
function getDocFolderItems(docFolderPath) {
|
||||
var result = {};
|
||||
var items = fs.readdirSync(path.resolve(docFolderPath));
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].endsWith('.md')) {
|
||||
var nameNoSuffix = path.basename(items[i], '.md');
|
||||
result[nameNoSuffix] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert an Angular-style name (eg, "card-view") into one with correct spaces and uppercase (eg, "Card View").
|
||||
function tidyName(name) {
|
||||
var result = name.replace(/-/g, " ");
|
||||
result = result.substr(0, 1).toUpperCase() + result.substr(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Generate the Markdown index for the files from the guide summary.
|
||||
function makeSummaryIndex() {
|
||||
var summaryJson = fs.readFileSync(summaryFileName, 'utf8');
|
||||
var summary = JSON.parse(summaryJson);
|
||||
|
||||
var result = '';
|
||||
|
||||
for (var i = 0; i < summary.length; i++) {
|
||||
var item = summary[i];
|
||||
result += '- [' + item.title + '](' + item.file + ')\n';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Create a stoplist of regular expressions.
|
||||
function makeStoplist(slFilePath) {
|
||||
var listExpressions = JSON.parse(fs.readFileSync(slFilePath, 'utf8'));
|
||||
|
||||
var result = [];
|
||||
|
||||
for (var i = 0; i < listExpressions.length; i++) {
|
||||
result.push(new RegExp(listExpressions[i]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if an item is covered by the stoplist and reject it if so.
|
||||
function rejectItemViaStoplist(stoplist, itemName) {
|
||||
for (var i = 0; i < stoplist.length; i++) {
|
||||
if (stoplist[i].test(itemName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function buildIndexSection(name, documented, undocumented) {
|
||||
var listItems = [];
|
||||
|
||||
if ((documented.length > 0) || (undocumented.length > 0)) {
|
||||
listItems.push('\n### ' + name + '\n');
|
||||
}
|
||||
|
||||
for (var i = 0; i < documented.length; i++) {
|
||||
var libFilePath = documented[i];
|
||||
var libFileName = path.basename(libFilePath, '.ts');
|
||||
var nameSections = libFileName.split('.');
|
||||
var visibleName = tidyName(nameSections[0]) + ' ' + nameSections[1];
|
||||
var mdListItem = '- [' + visibleName + '](' + libFileName + '.md)';
|
||||
listItems.push(mdListItem);
|
||||
}
|
||||
|
||||
for (var i = 0; i < undocumented.length; i++) {
|
||||
var libFilePath = undocumented[i].replace(/\\/g, '/');
|
||||
var libFileName = path.basename(libFilePath, '.ts');
|
||||
var nameSections = libFileName.split('.');
|
||||
var visibleName = tidyName(nameSections[0]) + ' ' + nameSections[1];
|
||||
var relPath = libFilePath.substr(libFilePath.indexOf('/lib'));
|
||||
var mdListItem = '- [*' + visibleName + '](..' + relPath + ')';
|
||||
listItems.push(mdListItem);
|
||||
}
|
||||
|
||||
return listItems;
|
||||
}
|
||||
|
||||
|
||||
var undocStoplist = makeStoplist(undocStoplistFileName);
|
||||
|
||||
var docDict = getDocFolderItems(path.resolve('..', 'docs'));
|
||||
|
||||
var rootItems = fs.readdirSync(path.resolve('.'));
|
||||
|
||||
var libs = {}
|
||||
|
||||
for (var i = 0; i < rootItems.length; i++) {
|
||||
var itemPath = path.resolve(rootItems[i]);
|
||||
var info = fs.statSync(itemPath);
|
||||
|
||||
if (info.isDirectory() && !rootItems[i].match(libFolderOmitRegex)) {
|
||||
libs[rootItems[i]] = {
|
||||
componentsWithDocs: [], componentsWithoutDocs: [],
|
||||
directivesWithDocs: [], directivesWithoutDocs: [],
|
||||
modelsWithDocs: [], modelsWithoutDocs: [],
|
||||
pipesWithDocs: [], pipesWithoutDocs: [],
|
||||
servicesWithDocs: [], servicesWithoutDocs: [],
|
||||
widgetsWithDocs: [], widgetsWithoutDocs: [],
|
||||
};
|
||||
|
||||
searchLibraryRecursive(libs[rootItems[i]], path.resolve(itemPath));
|
||||
}
|
||||
}
|
||||
|
||||
var indexFileText = fs.readFileSync(indexFileName, 'utf8');
|
||||
|
||||
var libNames = Object.keys(libs);
|
||||
|
||||
for (var i = 0; i < libNames.length; i++) {
|
||||
var libName = libNames[i];
|
||||
var libData = libs[libName];
|
||||
|
||||
var listItems = buildIndexSection('Components', libData.componentsWithDocs, libData.componentsWithoutDocs);
|
||||
listItems = listItems.concat(buildIndexSection('Directives', libData.directivesWithDocs, libData.directivesWithoutDocs));
|
||||
listItems = listItems.concat(buildIndexSection('Models', libData.modelsWithDocs, libData.modelsWithoutDocs));
|
||||
listItems = listItems.concat(buildIndexSection('Pipes', libData.pipesWithDocs, libData.pipesWithoutDocs));
|
||||
listItems = listItems.concat(buildIndexSection('Services', libData.servicesWithDocs, libData.servicesWithoutDocs));
|
||||
listItems = listItems.concat(buildIndexSection('Widgets', libData.widgetsWithDocs, libData.widgetsWithoutDocs));
|
||||
|
||||
var libText = listItems.join('\n');
|
||||
var libStartMarker = '<!-- ' + libName + ' start -->';
|
||||
var libEndMarker = '<!-- ' + libName + ' end -->';
|
||||
var contentRegex = new RegExp('(?:' + libStartMarker + ')([\\s\\S]*)(?:' + libEndMarker + ')');
|
||||
indexFileText = indexFileText.replace(contentRegex, libStartMarker + '\n' + libText + '\n' + libEndMarker);
|
||||
}
|
||||
|
||||
var guideStartMarker = '<!-- guide start -->';
|
||||
var guideEndMarker = '<!-- guide end -->';
|
||||
var contentRegex = new RegExp('(?:' + guideStartMarker + ')([\\s\\S]*)(?:' + guideEndMarker + ')');
|
||||
indexFileText = indexFileText.replace(contentRegex, guideStartMarker + '\n' + makeSummaryIndex() + '\n' + guideEndMarker);
|
||||
|
||||
fs.writeFileSync(indexFileName, indexFileText, 'utf-8');
|
||||
@@ -2,24 +2,24 @@ var Bundler = require('scss-bundle').Bundler;
|
||||
var writeFileSync = require('fs-extra').writeFileSync;
|
||||
var mkdirpSync = require('fs-extra').mkdirpSync;
|
||||
|
||||
new Bundler().Bundle('./core/styles/_index.scss', '**/*.scss').then(result => {
|
||||
new Bundler().Bundle('./lib/core/styles/_index.scss', '**/*.scss').then(result => {
|
||||
mkdirpSync('core');
|
||||
writeFileSync('dist/core/_theming.scss', result.bundledContent);
|
||||
writeFileSync('./lib/dist/core/_theming.scss', result.bundledContent);
|
||||
});
|
||||
|
||||
new Bundler().Bundle('./insights/styles/_index.scss', '**/*.scss').then(result => {
|
||||
new Bundler().Bundle('./lib/insights/styles/_index.scss', '**/*.scss').then(result => {
|
||||
mkdirpSync('insights');
|
||||
writeFileSync('dist/insights/_theming.scss', result.bundledContent);
|
||||
writeFileSync('./lib/dist/insights/_theming.scss', result.bundledContent);
|
||||
});
|
||||
|
||||
|
||||
new Bundler().Bundle('./process-services/styles/_index.scss', '**/*.scss').then(result => {
|
||||
new Bundler().Bundle('./lib/process-services/styles/_index.scss', '**/*.scss').then(result => {
|
||||
mkdirpSync('process-services');
|
||||
writeFileSync('dist/process-services/_theming.scss', result.bundledContent);
|
||||
writeFileSync('./lib/dist/process-services/_theming.scss', result.bundledContent);
|
||||
});
|
||||
|
||||
new Bundler().Bundle('./content-services/styles/_index.scss', '**/*.scss').then(result => {
|
||||
new Bundler().Bundle('./lib/content-services/styles/_index.scss', '**/*.scss').then(result => {
|
||||
mkdirpSync('content-services');
|
||||
writeFileSync('dist/content-services/_theming.scss', result.bundledContent);
|
||||
writeFileSync('./lib/dist/content-services/_theming.scss', result.bundledContent);
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"export-last-version": "export-new.json",
|
||||
"export-versions": [
|
||||
"export-2.0.0.json",
|
||||
"export-2.1.0.json",
|
||||
"export-2.2.0.json"
|
||||
]
|
||||
}
|
||||
@@ -1,373 +0,0 @@
|
||||
import * as ts from "typescript";
|
||||
import * as fs from "fs";
|
||||
import chalk from "chalk";
|
||||
|
||||
var nconf = require('nconf');
|
||||
var baseParh = "./config/export-check/";
|
||||
|
||||
nconf.add('config', { type: 'file', file: `${baseParh}export-check-config.json` });
|
||||
|
||||
var newLibExports = baseParh + nconf.get('export-last-version');
|
||||
var exportFilesVersions = nconf.get('export-versions').map((currentFile) => {
|
||||
return baseParh + currentFile;
|
||||
});
|
||||
|
||||
interface DocEntry {
|
||||
position?: {
|
||||
line: number,
|
||||
character: number,
|
||||
fileName: string
|
||||
},
|
||||
name?: string,
|
||||
skipError?: boolean
|
||||
};
|
||||
|
||||
let error_array = [];
|
||||
let warning_array = [];
|
||||
let exportedAllPath: Array<string> = [];
|
||||
let classList: Array<any> = [];
|
||||
|
||||
let add_error = function (error: string, nameClass: string) {
|
||||
let findErrorClass = false;
|
||||
|
||||
error_array.forEach((currentError) => {
|
||||
|
||||
if (currentError.nameClass === nameClass) {
|
||||
findErrorClass = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (!findErrorClass) {
|
||||
error_array.push({
|
||||
error: error,
|
||||
nameClass: nameClass
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let count_error = 0;
|
||||
let count_warning = 0;
|
||||
|
||||
let print_errors = function () {
|
||||
error_array.forEach((current_error) => {
|
||||
console.log(chalk.red(`[${++count_error}] ${current_error.error}\n`));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let add_warning = function (warning: string, nameClass: string, arrayCall: string[]) {
|
||||
|
||||
let findWarningClass = false;
|
||||
|
||||
warning_array.forEach((currentWarning) => {
|
||||
|
||||
if (currentWarning.nameClass === nameClass) {
|
||||
findWarningClass = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (!findWarningClass) {
|
||||
warning_array.push({
|
||||
warning: warning,
|
||||
nameClass: nameClass,
|
||||
arrayCall: arrayCall
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let print_warnings = function () {
|
||||
warning_array.forEach((current_warning) => {
|
||||
console.log(chalk.yellow(`[${++count_warning}] ${current_warning.warning} \n ${current_warning.arrayCall} \n`));
|
||||
});
|
||||
}
|
||||
|
||||
let currentErrorPostion = function (exportEntry) {
|
||||
return ` ${exportEntry.position.fileName} (${exportEntry.position.line},${exportEntry.position.character})`
|
||||
}
|
||||
|
||||
let check_export = function (exportLastMajor: any, exportNew: any) {
|
||||
|
||||
exportLastMajor.forEach((currentexportLastMajor) => {
|
||||
|
||||
let currentexportNew = exportNew.filter((currentexportNew) => {
|
||||
return currentexportNew.name === currentexportLastMajor.name;
|
||||
});
|
||||
|
||||
if (currentexportNew.length > 1) {
|
||||
|
||||
let arrayCall = [];
|
||||
|
||||
currentexportNew.forEach((error) => {
|
||||
arrayCall.push(`${currentErrorPostion(error)}`);
|
||||
})
|
||||
|
||||
add_warning(`Multiple export ${currentexportNew[0].name} times ${currentexportNew.length}`, currentexportNew[0].name, arrayCall);
|
||||
|
||||
} else if (currentexportNew.length === 0) {
|
||||
if (!currentexportLastMajor.skipError) {
|
||||
add_error(`Not find export ${currentexportLastMajor.name} , old path: [${currentErrorPostion(currentexportLastMajor)}]`, currentexportLastMajor.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let expandStarExport = function (node: ts.Node): ts.ExportDeclaration {
|
||||
const ed = node as ts.Node as ts.ExportDeclaration;
|
||||
const exports = [{ name: "x" }];
|
||||
const exportSpecifiers = exports.map(e => ts.createExportSpecifier(e.name, e.name));
|
||||
const exportClause = ts.createNamedExports(exportSpecifiers);
|
||||
const newEd = ts.updateExportDeclaration(ed, ed.decorators, ed.modifiers, exportClause, ed.moduleSpecifier);
|
||||
|
||||
return newEd as ts.ExportDeclaration
|
||||
};
|
||||
|
||||
/** Generate documentation for all classes in a set of .ts files */
|
||||
function generatExportList(fileNames: string[], options: ts.CompilerOptions): void {
|
||||
// Build a program using the set of root file names in fileNames
|
||||
let program = ts.createProgram(fileNames, options);
|
||||
// Get the checker, we will use it to find more about classes
|
||||
let checker = program.getTypeChecker();
|
||||
|
||||
let exportCurrentVersion: DocEntry[] = [];
|
||||
|
||||
// Visit every sourceFile in the program
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
if (!sourceFile.isDeclarationFile) {
|
||||
// Walk the tree to search for classes
|
||||
ts.forEachChild(sourceFile, visit);
|
||||
}
|
||||
}
|
||||
|
||||
classList.forEach((classNode) => {
|
||||
if (classNode.symbol.parent) {
|
||||
let pathClass = classNode.symbol.parent.escapedName.replace(/"/g, "");
|
||||
|
||||
exportedAllPath.forEach((currenPath) => {
|
||||
let pathNoExtension = currenPath.replace(/\.[^/.]+$/, "");
|
||||
|
||||
if (pathNoExtension === pathClass) {
|
||||
// console.log('pathClass'+ pathClass);
|
||||
// console.log('pathNoExtension '+ pathNoExtension);
|
||||
extractExport(classNode);
|
||||
return;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
exportCurrentVersion.sort((nameA, nameB) => nameA.name.localeCompare(nameB.name));
|
||||
|
||||
console.log(chalk.green(`Saving new export in ${newLibExports}`));
|
||||
|
||||
fs.writeFileSync(newLibExports, JSON.stringify(exportCurrentVersion, undefined, 4));
|
||||
|
||||
var exportNewJSON = JSON.parse(JSON.stringify(exportCurrentVersion));
|
||||
|
||||
exportFilesVersions.forEach((currentExportVersionFile) => {
|
||||
|
||||
error_array = [];
|
||||
warning_array = [];
|
||||
|
||||
try {
|
||||
var currentExportVersionJSON = JSON.parse(fs.readFileSync(`${currentExportVersionFile}`, 'utf8'));
|
||||
} catch (error) {
|
||||
console.log(chalk.red(`${currentExportVersionFile} json not present`));
|
||||
throw new Error(`Undetected export comapring file ${currentExportVersionFile}`);
|
||||
}
|
||||
|
||||
|
||||
console.log(chalk.green(`Comparing ${newLibExports} and ${currentExportVersionFile}`));
|
||||
|
||||
check_export(currentExportVersionJSON, exportNewJSON);
|
||||
|
||||
print_warnings();
|
||||
print_errors();
|
||||
|
||||
|
||||
if (error_array.length > 0) {
|
||||
throw new Error('Export problems detected');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
function extractExport(node: ts.Node) {
|
||||
//skip file with export-check: exclude comment
|
||||
if (node.getFullText(node.getSourceFile()).indexOf('export-check: exclude') > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let { line, character } = node.getSourceFile().getLineAndCharacterOfPosition(node.getStart());
|
||||
//console.log(line + " " + character + " " + node.getSourceFile().fileName);
|
||||
|
||||
let symbol = checker.getSymbolAtLocation(node);
|
||||
|
||||
if (symbol) {
|
||||
let arryCalls = recursiveStackSave(node);
|
||||
|
||||
let className: any = symbol.escapedName;
|
||||
let filename = node.getSourceFile().fileName.substring(node.getSourceFile().fileName.indexOf('lib'), node.getSourceFile().fileName.length);
|
||||
exportCurrentVersion.push(serializeClass(className, line, character, filename, arryCalls));
|
||||
|
||||
// if (className === "ContentMetadataService") {
|
||||
// console.log(chalk.red("exportedAllPath" + exportedAllPath));
|
||||
// console.log(chalk.red("ContentMetadataService"));
|
||||
// recursiveStack(node);
|
||||
// }
|
||||
|
||||
} else {
|
||||
|
||||
let arryCalls = recursiveStackSave(node);
|
||||
|
||||
let className: any = (node as ts.ClassDeclaration).name.escapedText;
|
||||
let filename = node.getSourceFile().fileName.substring(node.getSourceFile().fileName.indexOf('lib'), node.getSourceFile().fileName.length);
|
||||
exportCurrentVersion.push(serializeClass(className, line, character, filename, arryCalls));
|
||||
|
||||
// if (className === "ContentMetadataService") {
|
||||
// console.log(chalk.greenBright("exportedAllPath" + exportedAllPath));
|
||||
// console.log(chalk.greenBright("ContentMetadataService"));
|
||||
// recursiveStack(node);
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function recursiveStackSave(node: ts.Node, arrayCalls?: string[]) {
|
||||
if (!arrayCalls) {
|
||||
arrayCalls = [];
|
||||
}
|
||||
|
||||
let filename = node.getSourceFile().fileName.substring(node.getSourceFile().fileName.indexOf('lib'), node.getSourceFile().fileName.length);
|
||||
let { line, character } = node.getSourceFile().getLineAndCharacterOfPosition(node.getStart());
|
||||
|
||||
arrayCalls.push(node.getSourceFile().fileName);
|
||||
|
||||
if (node.parent) {
|
||||
recursiveStackSave(node.parent, arrayCalls)
|
||||
}
|
||||
|
||||
return arrayCalls;
|
||||
|
||||
}
|
||||
|
||||
function recursiveStack(node: ts.Node) {
|
||||
let filename = node.getSourceFile().fileName.substring(node.getSourceFile().fileName.indexOf('lib'), node.getSourceFile().fileName.length);
|
||||
let { line, character } = node.getSourceFile().getLineAndCharacterOfPosition(node.getStart());
|
||||
console.log(chalk.bgCyan(line + " " + character + " " + node.getSourceFile().fileName));
|
||||
|
||||
if (node.parent) {
|
||||
recursiveStack(node.parent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** visit nodes finding exported classes */
|
||||
function visit(node: ts.Node) {
|
||||
// Only consider exported nodes
|
||||
|
||||
if (node.kind === ts.SyntaxKind.ClassDeclaration) {
|
||||
|
||||
if (node.decorators) {
|
||||
node.decorators.forEach((decorator) => {
|
||||
visit(decorator as ts.Node);
|
||||
});
|
||||
}
|
||||
|
||||
classList.push(node);
|
||||
}
|
||||
|
||||
if (node.kind === ts.SyntaxKind.PropertyAssignment) {
|
||||
const initializer = (node as ts.PropertyAssignment).initializer;
|
||||
|
||||
visit(initializer as ts.Node);
|
||||
}
|
||||
|
||||
if (node.kind === ts.SyntaxKind.Identifier) {
|
||||
extractExport(node);
|
||||
}
|
||||
|
||||
if (node.kind === ts.SyntaxKind.ArrayLiteralExpression) {
|
||||
(node as ts.ArrayLiteralExpression).elements.forEach((element) => {
|
||||
visit(element as ts.Node);
|
||||
});
|
||||
}
|
||||
|
||||
if (node.kind === ts.SyntaxKind.Decorator &&
|
||||
((node as ts.Decorator).expression as any).expression.text === "NgModule") {
|
||||
|
||||
((node as ts.Decorator).expression as any).arguments.forEach((argument) => {
|
||||
argument.properties.forEach((property) => {
|
||||
if (property.name.escapedText === "exports") {
|
||||
visit(property as ts.Node);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (ts.isExportDeclaration(node)) {
|
||||
if (node.exportClause) {
|
||||
node.exportClause.elements.forEach(exportCurrent => {
|
||||
extractExport(exportCurrent as ts.Node);
|
||||
});
|
||||
} else {
|
||||
(node.parent as any).resolvedModules.forEach((currentModule) => {
|
||||
|
||||
if (currentModule) {
|
||||
let find;
|
||||
exportedAllPath.forEach((currentExported) => {
|
||||
if (currentModule.resolvedFileName === currentExported) {
|
||||
find = currentExported;
|
||||
}
|
||||
})
|
||||
|
||||
if (!find) {
|
||||
exportedAllPath.push(currentModule.resolvedFileName);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
visit(node.moduleSpecifier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ts.isModuleDeclaration(node)) {
|
||||
// This is a namespace, visit its children
|
||||
ts.forEachChild(node, visit);
|
||||
}
|
||||
}
|
||||
|
||||
/** Serialize a symbol into a json object */
|
||||
function serializeSymbol(className: string, line?: number, character?: number, fileName?: string, arryCalls?: string[]): DocEntry {
|
||||
|
||||
return {
|
||||
position: {
|
||||
line: line,
|
||||
character: character,
|
||||
fileName: fileName
|
||||
},
|
||||
name: className
|
||||
};
|
||||
}
|
||||
|
||||
/** Serialize a class symbol information */
|
||||
function serializeClass(className: string, line?: number, character?: number, fileName?: string, arryCalls?: string[]) {
|
||||
let details = serializeSymbol(className, line, character, fileName, arryCalls);
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
/** True if this is visible outside this file, false otherwise */
|
||||
function isNodeExported(node: ts.Node): boolean {
|
||||
return (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export) !== 0 || (!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
generatExportList(process.argv.slice(2), {
|
||||
target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS, removeComments: false
|
||||
});
|
||||
@@ -1,96 +0,0 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
var docFolderPath = path.resolve("..", "docs");
|
||||
var graphFileName = path.resolve(docFolderPath, "seeAlsoGraph.json");
|
||||
|
||||
|
||||
// Get a list of all items that have a file in the docs folder.
|
||||
function getDocFolderItems(docFolderPath) {
|
||||
var result = {};
|
||||
var items = fs.readdirSync(path.resolve(docFolderPath));
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].endsWith('.md')) {
|
||||
var nameNoSuffix = path.basename(items[i], '.md');
|
||||
result[nameNoSuffix] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Convert an Angular-style name (eg, "card-view") into one with correct spaces and uppercase (eg, "Card view").
|
||||
function tidyName(name) {
|
||||
var result = name.replace(/-/g, " ");
|
||||
result = result.substr(0, 1).toUpperCase() + result.substr(1);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function buildSeeAlsoList(arcs) {
|
||||
var listItems = [];
|
||||
|
||||
for (var i = 0; i < arcs.length; i++) {
|
||||
var parts = arcs[i].split('.');
|
||||
var itemName = tidyName(parts[0]);
|
||||
|
||||
if (parts[1]) {
|
||||
itemName += ' ' + parts[1];
|
||||
}
|
||||
|
||||
listItems.push('- [' + itemName + '](' + arcs[i] + '.md)');
|
||||
}
|
||||
|
||||
return listItems.join('\n');
|
||||
}
|
||||
|
||||
|
||||
// If item is not in the arcs array then add it at
|
||||
// the end.
|
||||
function fixArcs(arcsArray, item) {
|
||||
if (arcsArray.indexOf(item) == -1) {
|
||||
arcsArray.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Makes link symmetrical between items (ie, if A links to B but not the other way
|
||||
// around then it adds the missing link).
|
||||
function tidyGraph(graph) {
|
||||
var nodeNames = Object.keys(graph);
|
||||
|
||||
for (var n = 0; n < nodeNames.length; n++) {
|
||||
var currNodeName = nodeNames[n];
|
||||
|
||||
var currNodeArcs = graph[currNodeName];
|
||||
|
||||
for (var a = 0; a < currNodeArcs.length; a++) {
|
||||
var linkedNode = graph[currNodeArcs[a]];
|
||||
fixArcs(linkedNode, currNodeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var graphJson = fs.readFileSync(graphFileName, 'utf8');
|
||||
var graph = JSON.parse(graphJson);
|
||||
tidyGraph(graph);
|
||||
|
||||
var nodeNames = Object.keys(graph);
|
||||
|
||||
for (var i = 0; i < nodeNames.length; i++) {
|
||||
var seeAlsoText = '## See also\n\n' + buildSeeAlsoList(graph[nodeNames[i]]);
|
||||
|
||||
var docFileName = path.resolve(docFolderPath, nodeNames[i] + '.md');
|
||||
var docFileText = fs.readFileSync(docFileName, 'utf8');
|
||||
|
||||
var seeAlsoStartMarker = '<!-- seealso start -->';
|
||||
var seeAlsoEndMarker = '<!-- seealso end -->';
|
||||
|
||||
var seeAlsoRegex = new RegExp('(?:' + seeAlsoStartMarker + ')([\\s\\S]*)(?:' + seeAlsoEndMarker + ')');
|
||||
docFileText = docFileText.replace(seeAlsoRegex, seeAlsoStartMarker + '\n' + seeAlsoText + '\n' + seeAlsoEndMarker);
|
||||
|
||||
fs.writeFileSync(docFileName, docFileText, 'utf-8');
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
const webpackCoverage = require('./webpack.coverage');
|
||||
const webpackTest= require('./webpack.test');
|
||||
const webpackTest = require('./webpack.test');
|
||||
|
||||
module.exports = function (config) {
|
||||
var _config = {
|
||||
basePath: './',
|
||||
basePath: '../',
|
||||
|
||||
frameworks: ['jasmine-ajax', 'jasmine'],
|
||||
|
||||
@@ -13,6 +13,11 @@ module.exports = function (config) {
|
||||
{pattern: './node_modules/hammerjs/hammer.min.js', included: true, watched: false},
|
||||
{pattern: './node_modules/hammerjs/hammer.min.js.map', included: false, watched: false},
|
||||
|
||||
// pdf-js
|
||||
{pattern: './node_modules/pdfjs-dist/build/pdf.js', included: true, watched: false},
|
||||
{pattern: './node_modules/pdfjs-dist/build/pdf.worker.js', included: true, watched: false},
|
||||
{pattern: './node_modules/pdfjs-dist/web/pdf_viewer.js', included: true, watched: false},
|
||||
|
||||
{
|
||||
pattern: './node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
|
||||
included: true,
|
||||
@@ -31,23 +36,23 @@ module.exports = function (config) {
|
||||
watched: false
|
||||
},
|
||||
|
||||
// pdf-js
|
||||
{pattern: './node_modules/pdfjs-dist/build/pdf.js', included: true, watched: false},
|
||||
{pattern: './node_modules/pdfjs-dist/build/pdf.worker.js', included: true, watched: false},
|
||||
{pattern: './node_modules/pdfjs-dist/web/pdf_viewer.js', included: true, watched: false},
|
||||
{pattern: './lib/core/i18n/**/en.json', included: false, served: true, watched: false},
|
||||
{pattern: './lib/content-services/i18n/**/en.json', included: false, served: true, watched: false},
|
||||
{pattern: './lib/process-services/i18n/**/en.json', included: false, served: true, watched: false},
|
||||
|
||||
{pattern: config.component + '/karma-test-shim.js', watched: false},
|
||||
{pattern: './lib/**/*.ts', included: false, served: true, watched: false},
|
||||
|
||||
{pattern: './core/i18n/**/en.json', included: false, served: true, watched: false},
|
||||
{pattern: './content-services/i18n/**/en.json', included: false, served: true, watched: false},
|
||||
{pattern: './process-services/i18n/**/en.json', included: false, served: true, watched: false},
|
||||
{pattern: './lib/config/app.config.json', included: false, served: true, watched: false},
|
||||
{pattern: './lib/core/viewer/assets/fake-test-file.pdf', included: false, served: true, watched: false},
|
||||
{pattern: './lib/core/viewer/assets/fake-test-file.txt', included: false, served: true, watched: false},
|
||||
{
|
||||
pattern: './lib/core/viewer/assets/fake-test-password-file.pdf',
|
||||
included: false,
|
||||
served: true,
|
||||
watched: false
|
||||
},
|
||||
|
||||
{pattern: './**/*.ts', included: false, served: true, watched: false},
|
||||
|
||||
{pattern: './config/app.config.json', included: false, served: true, watched: false},
|
||||
{pattern: './core/viewer/assets/fake-test-file.pdf', included: false, served: true, watched: false},
|
||||
{pattern: './core/viewer/assets/fake-test-file.txt', included: false, served: true, watched: false},
|
||||
{pattern: './core/viewer/assets/fake-test-password-file.pdf', included: false, served: true, watched: false}
|
||||
{pattern: './lib/' + config.component + '/karma-test-shim.js', watched: false}
|
||||
],
|
||||
|
||||
webpack: (config.mode === 'coverage') ? webpackCoverage(config) : webpackTest(config),
|
||||
@@ -62,10 +67,10 @@ module.exports = function (config) {
|
||||
port: 9876,
|
||||
|
||||
proxies: {
|
||||
'/app.config.json': '/base/config/app.config.json',
|
||||
'/fake-test-file.pdf': '/base/core/viewer/assets/fake-test-file.pdf',
|
||||
'/fake-test-file.txt': '/base/core/viewer/assets/fake-test-file.txt',
|
||||
'/fake-test-password-file.pdf': '/base/core/viewer/assets/fake-test-password-file.pdf'
|
||||
'/app.config.json': '/base/lib/config/app.config.json',
|
||||
'/fake-test-file.pdf': '/base/lib/core/viewer/assets/fake-test-file.pdf',
|
||||
'/fake-test-file.txt': '/base/lib/core/viewer/assets/fake-test-file.txt',
|
||||
'/fake-test-password-file.pdf': '/base/lib/core/viewer/assets/fake-test-password-file.pdf'
|
||||
},
|
||||
|
||||
// level of logging
|
||||
@@ -80,17 +85,30 @@ module.exports = function (config) {
|
||||
browserNoActivityTimeout: 2400000,
|
||||
captureTimeout: 1200000,
|
||||
browsers: ['Chrome'],
|
||||
// browsers: ['ChromeHeadless'],
|
||||
|
||||
customLaunchers: {
|
||||
ChromeHeadless: {
|
||||
base: 'Chrome',
|
||||
flags: [
|
||||
'--no-sandbox',
|
||||
'--headless',
|
||||
'--disable-gpu',
|
||||
'--remote-debugging-port=9222'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Karma plugins loaded
|
||||
plugins: [
|
||||
require('../node_modules/karma-jasmine'),
|
||||
require('../node_modules/karma-coverage'),
|
||||
require('../node_modules/karma-sourcemap-loader'),
|
||||
require('../node_modules/karma-jasmine-ajax'),
|
||||
require('../node_modules/karma-chrome-launcher'),
|
||||
require('../node_modules/karma-webpack'),
|
||||
require('../node_modules/karma-jasmine-html-reporter'),
|
||||
require('../node_modules/karma-mocha-reporter')
|
||||
require('../../node_modules/karma-jasmine'),
|
||||
require('../../node_modules/karma-coverage'),
|
||||
require('../../node_modules/karma-sourcemap-loader'),
|
||||
require('../../node_modules/karma-jasmine-ajax'),
|
||||
require('../../node_modules/karma-chrome-launcher'),
|
||||
require('../../node_modules/karma-webpack'),
|
||||
require('../../node_modules/karma-jasmine-html-reporter'),
|
||||
require('../../node_modules/karma-mocha-reporter')
|
||||
],
|
||||
|
||||
webpackServer: {
|
||||
@@ -100,6 +118,10 @@ module.exports = function (config) {
|
||||
// Coverage reporter generates the coverage
|
||||
reporters: ['mocha', 'coverage', 'kjhtml'],
|
||||
|
||||
mochaReporter: {
|
||||
ignoreSkipped: true
|
||||
},
|
||||
|
||||
preprocessors: {
|
||||
'**/karma-test-shim.js': ['webpack'],
|
||||
'(core|content-services|process-services)/**/!(*spec|index|*mock|*model|*event).js': 'coverage'
|
||||
@@ -107,7 +129,7 @@ module.exports = function (config) {
|
||||
|
||||
coverageReporter: {
|
||||
includeAllSources: true,
|
||||
dir: './coverage/' + config.component + '/',
|
||||
dir: './lib/coverage/' + config.component + '/',
|
||||
subdir: 'report',
|
||||
reporters: [
|
||||
{type: 'text'},
|
||||
@@ -117,6 +139,12 @@ module.exports = function (config) {
|
||||
{type: 'lcov'}
|
||||
]
|
||||
}
|
||||
|
||||
// client: {
|
||||
// jasmine: {
|
||||
// random: true
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
config.set(_config);
|
||||
|
||||
@@ -37,7 +37,7 @@ module.exports = {
|
||||
"@alfresco/adf-insights": path.resolve(__dirname, '../insights/index.ts')
|
||||
},
|
||||
extensions: ['.ts', '.js', '.scss'],
|
||||
modules: [helpers.root('node_modules')]
|
||||
modules: [helpers.root('../node_modules')]
|
||||
},
|
||||
|
||||
module: {
|
||||
@@ -149,8 +149,8 @@ module.exports = {
|
||||
new CopyWebpackPlugin([
|
||||
... alfrescoLibs.map(lib => {
|
||||
return {
|
||||
from: `${lib}/i18n/`,
|
||||
to: `${lib}/bundles/assets/adf-${lib}/i18n/`
|
||||
from: `lib/${lib}/i18n/`,
|
||||
to: `lib/${lib}/bundles/assets/adf-${lib}/i18n/`
|
||||
}
|
||||
})
|
||||
]),
|
||||
|
||||
+10
-10
@@ -1,19 +1,19 @@
|
||||
const ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||
|
||||
const extractScss = new ExtractTextPlugin('./dist/core/prebuilt-themes/[name].css');
|
||||
const extractScss = new ExtractTextPlugin('./lib/dist/core/prebuilt-themes/[name].css');
|
||||
|
||||
module.exports = {
|
||||
|
||||
entry: {
|
||||
'adf-blue-orange': './core/styles/prebuilt/adf-blue-orange.scss',
|
||||
'adf-blue-purple': './core/styles/prebuilt/adf-blue-purple.scss',
|
||||
'adf-cyan-orange': './core/styles/prebuilt/adf-cyan-orange.scss',
|
||||
'adf-cyan-purple': './core/styles/prebuilt/adf-cyan-purple.scss',
|
||||
'adf-green-purple': './core/styles/prebuilt/adf-green-purple.scss',
|
||||
'adf-green-orange': './core/styles/prebuilt/adf-green-orange.scss',
|
||||
'adf-pink-bluegrey': './core/styles/prebuilt/adf-pink-bluegrey.scss',
|
||||
'adf-indigo-pink': './core/styles/prebuilt/adf-indigo-pink.scss',
|
||||
'adf-purple-green': './core/styles/prebuilt/adf-purple-green.scss'
|
||||
'adf-blue-orange': './lib/core/styles/prebuilt/adf-blue-orange.scss',
|
||||
'adf-blue-purple': './lib/core/styles/prebuilt/adf-blue-purple.scss',
|
||||
'adf-cyan-orange': './lib/core/styles/prebuilt/adf-cyan-orange.scss',
|
||||
'adf-cyan-purple': './lib/core/styles/prebuilt/adf-cyan-purple.scss',
|
||||
'adf-green-purple': './lib/core/styles/prebuilt/adf-green-purple.scss',
|
||||
'adf-green-orange': './lib/core/styles/prebuilt/adf-green-orange.scss',
|
||||
'adf-pink-bluegrey': './lib/core/styles/prebuilt/adf-pink-bluegrey.scss',
|
||||
'adf-indigo-pink': './lib/core/styles/prebuilt/adf-indigo-pink.scss',
|
||||
'adf-purple-green': './lib/core/styles/prebuilt/adf-purple-green.scss'
|
||||
},
|
||||
|
||||
output: {
|
||||
|
||||
@@ -1,13 +1,40 @@
|
||||
<div *ngIf="folderNode" data-automation-id="breadcrumb" class="adf-breadcrumb-container">
|
||||
<li *ngFor="let item of route; let last = last"
|
||||
<nav *ngIf="folderNode" data-automation-id="breadcrumb" class="adf-breadcrumb-container" role="list">
|
||||
<button
|
||||
*ngIf="hasPreviousNodes()"
|
||||
tabindex="0"
|
||||
class="adf-breadcrumb-dropdown-trigger"
|
||||
(click)="open()">
|
||||
<div class="adf-breadcrumb-dropdown-trigger-icon">
|
||||
<mat-icon [class.isRoot]="!hasPreviousNodes()">folder</mat-icon>
|
||||
<mat-icon [class.isRoot]="!hasPreviousNodes()" class="adf-breadcrumb-dropdown-trigger-arrow">arrow_drop_down</mat-icon>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<mat-select
|
||||
#dropdown
|
||||
*ngIf="hasPreviousNodes()"
|
||||
class="adf-breadcrumb-dropdown-path"
|
||||
tabindex="0">
|
||||
|
||||
<mat-option
|
||||
*ngFor="let node of previousNodes;"
|
||||
(click)="onRoutePathClick(node, $event)"
|
||||
class="adf-breadcrumb-path-option"
|
||||
tabindex="0">
|
||||
{{ node.name | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
|
||||
<div *ngFor="let item of lastNodes; let last = last"
|
||||
[class.active]="last"
|
||||
[ngSwitch]="last"
|
||||
title="{{ item.name | translate }}"
|
||||
class="adf-breadcrumb-item">
|
||||
class="adf-breadcrumb-item"
|
||||
role="listitem">
|
||||
|
||||
<a *ngSwitchDefault href="#" [attr.data-automation-id]="'breadcrumb_' + item.name"
|
||||
class="adf-breadcrumb-item-anchor"
|
||||
(click)="onRoutePathClick(item, $event)">
|
||||
class="adf-breadcrumb-item-anchor"
|
||||
(click)="onRoutePathClick(item, $event)">
|
||||
{{ item.name | translate }}
|
||||
</a>
|
||||
|
||||
@@ -18,12 +45,13 @@
|
||||
<mat-icon class="adf-breadcrumb-item-chevron" *ngIf="!last">
|
||||
chevron_right
|
||||
</mat-icon>
|
||||
</li>
|
||||
</div>
|
||||
<div *ngIf="!folderNode && hasRoot">
|
||||
<li class="adf-breadcrumb-item">
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<nav *ngIf="!folderNode && hasRoot" data-automation-id="breadcrumb" role="navigation">
|
||||
<div class="adf-breadcrumb-item active" role="listitem">
|
||||
<div class="adf-breadcrumb-item-current">
|
||||
{{ root | translate }}
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
$breadcrumb-chevron-spacer: 2px;
|
||||
|
||||
@mixin adf-breadcrumb-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$accent: map-get($theme, accent);
|
||||
$warn: map-get($theme, warn);
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
|
||||
$breadcrumb-chevron-spacer: 2px;
|
||||
|
||||
.adf-breadcrumb {
|
||||
display: flex;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.2px;
|
||||
letter-spacing: -0.2px;
|
||||
color: mat-color($foreground, text, 0.54);
|
||||
overflow: hidden;
|
||||
|
||||
&-container {
|
||||
margin: 0;
|
||||
@@ -20,32 +24,92 @@ $breadcrumb-chevron-spacer: 2px;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
&-dropdown {
|
||||
&-path {
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
margin-top: 35px;
|
||||
|
||||
&.mat-select {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-trigger {
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
width: 30px;
|
||||
margin-top: 2px;
|
||||
margin-right: 5px;
|
||||
|
||||
&:focus {
|
||||
color: mat-color($primary);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
font-size: 17px;
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
top: 4px;
|
||||
color: white;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&-arrow.isRoot {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&-arrow.focus {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-trigger.isRoot {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
&-item {
|
||||
padding-right: $breadcrumb-chevron-spacer;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
line-height: 24px;
|
||||
|
||||
line-height: 33px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.2px;
|
||||
text-align: left;
|
||||
opacity: 0.6;
|
||||
|
||||
flex: 0 10 auto;
|
||||
min-width: 35px;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.active {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
flex: 1 1 auto;
|
||||
color: mat-color($foreground, text, 0.87);
|
||||
min-width: initial;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&-chevron {
|
||||
opacity: 1;
|
||||
margin-top: 9px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
&.mat-primary {
|
||||
@@ -61,17 +125,27 @@ $breadcrumb-chevron-spacer: 2px;
|
||||
}
|
||||
|
||||
&-anchor {
|
||||
box-sizing: border-box;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
&-current {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&-path-option {
|
||||
&.mat-option {
|
||||
background-color: mat-color($background, card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
*/
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { PathElementEntity } from 'alfresco-js-api';
|
||||
import { DataTableModule } from '@alfresco/adf-core';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { fakeNodeWithCreatePermission } from '../mock';
|
||||
import { CustomResourcesService, DocumentListService, DocumentListComponent } from '../document-list';
|
||||
import { DocumentListComponent } from '../document-list';
|
||||
import { BreadcrumbComponent } from './breadcrumb.component';
|
||||
|
||||
declare let jasmine: any;
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
|
||||
describe('Breadcrumb', () => {
|
||||
|
||||
@@ -31,33 +30,21 @@ describe('Breadcrumb', () => {
|
||||
let fixture: ComponentFixture<BreadcrumbComponent>;
|
||||
let documentList: DocumentListComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent,
|
||||
BreadcrumbComponent
|
||||
],
|
||||
providers: [
|
||||
DocumentListService,
|
||||
CustomResourcesService
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(BreadcrumbComponent);
|
||||
|
||||
component = fixture.componentInstance;
|
||||
|
||||
documentList = TestBed.createComponent<DocumentListComponent>(DocumentListComponent).componentInstance;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should prevent default click behavior', () => {
|
||||
let event = jasmine.createSpyObj('event', ['preventDefault']);
|
||||
component.onRoutePathClick(null, event);
|
||||
|
||||
@@ -15,16 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
ViewEncapsulation,
|
||||
OnInit
|
||||
} from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { MatSelect } from '@angular/material';
|
||||
import { MinimalNodeEntryEntity, PathElementEntity } from 'alfresco-js-api';
|
||||
import { DocumentListComponent } from '../document-list';
|
||||
|
||||
@@ -70,6 +62,16 @@ export class BreadcrumbComponent implements OnInit, OnChanges {
|
||||
@Input()
|
||||
transform: (node) => any;
|
||||
|
||||
@ViewChild('dropdown')
|
||||
dropdown: MatSelect;
|
||||
|
||||
/** Maximum number of nodes to display before wrapping them with a dropdown element. */
|
||||
@Input()
|
||||
maxItems: number;
|
||||
|
||||
previousNodes: PathElementEntity[];
|
||||
lastNodes: PathElementEntity[];
|
||||
|
||||
route: PathElementEntity[] = [];
|
||||
|
||||
get hasRoot(): boolean {
|
||||
@@ -78,7 +80,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges {
|
||||
|
||||
/** Emitted when the user clicks on a breadcrumb. */
|
||||
@Output()
|
||||
navigate: EventEmitter<PathElementEntity> = new EventEmitter<PathElementEntity>();
|
||||
navigate = new EventEmitter<PathElementEntity>();
|
||||
|
||||
ngOnInit() {
|
||||
this.transform = this.transform ? this.transform : null;
|
||||
@@ -95,6 +97,28 @@ export class BreadcrumbComponent implements OnInit, OnChanges {
|
||||
let node = this.transform ? this.transform(this.folderNode) : this.folderNode;
|
||||
this.route = this.parseRoute(node);
|
||||
}
|
||||
this.recalculateNodes();
|
||||
}
|
||||
|
||||
protected recalculateNodes(): void {
|
||||
if (this.maxItems && this.route.length > this.maxItems) {
|
||||
this.lastNodes = this.route.slice(this.route.length - this.maxItems);
|
||||
this.previousNodes = this.route.slice(0, this.route.length - this.maxItems);
|
||||
this.previousNodes.reverse();
|
||||
} else {
|
||||
this.lastNodes = this.route;
|
||||
this.previousNodes = null;
|
||||
}
|
||||
}
|
||||
|
||||
open(): void {
|
||||
if (this.dropdown) {
|
||||
this.dropdown.open();
|
||||
}
|
||||
}
|
||||
|
||||
hasPreviousNodes(): boolean {
|
||||
return this.previousNodes ? true : false;
|
||||
}
|
||||
|
||||
parseRoute(node: MinimalNodeEntryEntity): PathElementEntity[] {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<button
|
||||
tabindex="0"
|
||||
class="adf-dropdown-breadcumb-trigger"
|
||||
class="adf-dropdown-breadcrumb-trigger"
|
||||
(click)="open()"
|
||||
data-automation-id="dropdown-breadcrumb-trigger">
|
||||
<mat-icon [class.isRoot]="!hasPreviousNodes()">folder</mat-icon>
|
||||
@@ -10,7 +10,7 @@
|
||||
<mat-icon class="adf-dropdown-breadcrumb-item-chevron">chevron_right</mat-icon>
|
||||
|
||||
<mat-select
|
||||
#select
|
||||
#dropdown
|
||||
*ngIf="hasPreviousNodes()"
|
||||
class="adf-dropdown-breadcrumb-path"
|
||||
tabindex="0"
|
||||
@@ -22,7 +22,7 @@
|
||||
class="adf-dropdown-breadcrumb-path-option"
|
||||
tabindex="0"
|
||||
data-automation-class="dropdown-breadcrumb-path-option">
|
||||
{{ node.name }}
|
||||
{{ node.name | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
@mixin adf-breadcrumb-dropdown-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$dropdownHorizontalOffset: 30px;
|
||||
@@ -10,18 +9,23 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-dropdown-breadcumb-trigger {
|
||||
&-dropdown-breadcrumb-trigger {
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
|
||||
&:focus {
|
||||
color: mat-color($primary);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-breadcrumb-item-chevron {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&-dropdown-breadcumb-trigger.isRoot {
|
||||
&-dropdown-breadcrumb-trigger.isRoot {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -39,7 +43,6 @@
|
||||
|
||||
&-current-folder {
|
||||
text-align: left;
|
||||
line-height: 33px;
|
||||
margin-left: $dropdownHorizontalOffset;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, SimpleChange } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DataTableModule } from '@alfresco/adf-core';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { fakeNodeWithCreatePermission } from '../mock';
|
||||
import { CustomResourcesService, DocumentListComponent, DocumentListService } from '../document-list';
|
||||
import { DocumentListComponent } from '../document-list';
|
||||
import { DropdownBreadcrumbComponent } from './dropdown-breadcrumb.component';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
|
||||
describe('DropdownBreadcrumb', () => {
|
||||
|
||||
@@ -29,126 +30,156 @@ describe('DropdownBreadcrumb', () => {
|
||||
let fixture: ComponentFixture<DropdownBreadcrumbComponent>;
|
||||
let documentList: DocumentListComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent,
|
||||
DropdownBreadcrumbComponent
|
||||
],
|
||||
providers: [
|
||||
DocumentListService,
|
||||
CustomResourcesService
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DropdownBreadcrumbComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
documentList = TestBed.createComponent<DocumentListComponent>(DocumentListComponent).componentInstance;
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(DropdownBreadcrumbComponent);
|
||||
component = fixture.componentInstance;
|
||||
documentList = TestBed.createComponent<DocumentListComponent>(DocumentListComponent).componentInstance;
|
||||
}));
|
||||
|
||||
afterEach(async(() => {
|
||||
fixture.destroy();
|
||||
}));
|
||||
|
||||
function openSelect() {
|
||||
const folderIcon = fixture.debugElement.query(By.css('[data-automation-id="dropdown-breadcrumb-trigger"]'));
|
||||
folderIcon.triggerEventHandler('click', null);
|
||||
const folderIcon = fixture.debugElement.nativeElement.querySelector('[data-automation-id="dropdown-breadcrumb-trigger"]');
|
||||
folderIcon.click();
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
function triggerComponentChange(fakeNodeData) {
|
||||
const change = new SimpleChange(null, fakeNodeData, true);
|
||||
component.ngOnChanges({'folderNode': change});
|
||||
component.ngOnChanges({ 'folderNode': change });
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
function clickOnTheFirstOption() {
|
||||
const option = fixture.debugElement.query(By.css('[data-automation-class="dropdown-breadcrumb-path-option"]'));
|
||||
option.triggerEventHandler('click', null);
|
||||
const option: any = document.querySelector('[id^="mat-option"]');
|
||||
option.click();
|
||||
}
|
||||
|
||||
it('should display only the current folder name if there is no previous folders', () => {
|
||||
fakeNodeWithCreatePermission.path.elements = [];
|
||||
it('should display only the current folder name if there is no previous folders', (done) => {
|
||||
let fakeNodeWithCreatePermissionInstance = JSON.parse(JSON.stringify(fakeNodeWithCreatePermission));
|
||||
fakeNodeWithCreatePermissionInstance.path.elements = [];
|
||||
|
||||
triggerComponentChange(fakeNodeWithCreatePermission);
|
||||
openSelect();
|
||||
triggerComponentChange(fakeNodeWithCreatePermissionInstance);
|
||||
|
||||
const currentFolder = fixture.debugElement.query(By.css('[data-automation-id="current-folder"]'));
|
||||
const path = fixture.debugElement.query(By.css('[data-automation-id="dropdown-breadcrumb-path"]'));
|
||||
expect(path).toBeNull();
|
||||
expect(currentFolder).not.toBeNull();
|
||||
expect(currentFolder.nativeElement.innerText.trim()).toEqual('Test');
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
openSelect();
|
||||
|
||||
const currentFolder = fixture.debugElement.query(By.css('[data-automation-id="current-folder"]'));
|
||||
const path = fixture.debugElement.query(By.css('[data-automation-id="dropdown-breadcrumb-path"]'));
|
||||
expect(path).toBeNull();
|
||||
expect(currentFolder).not.toBeNull();
|
||||
expect(currentFolder.nativeElement.innerText.trim()).toEqual('Test');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display only the path in the selectbox', () => {
|
||||
fakeNodeWithCreatePermission.path.elements = [
|
||||
it('should display only the path in the selectbox', (done) => {
|
||||
let fakeNodeWithCreatePermissionInstance = JSON.parse(JSON.stringify(fakeNodeWithCreatePermission));
|
||||
fakeNodeWithCreatePermissionInstance.path.elements = [
|
||||
{ id: '1', name: 'Stark Industries' },
|
||||
{ id: '2', name: 'User Homes' },
|
||||
{ id: '3', name: 'J.A.R.V.I.S' }
|
||||
];
|
||||
|
||||
triggerComponentChange(fakeNodeWithCreatePermission);
|
||||
openSelect();
|
||||
triggerComponentChange(fakeNodeWithCreatePermissionInstance);
|
||||
|
||||
const path = fixture.debugElement.query(By.css('[data-automation-id="dropdown-breadcrumb-path"]'));
|
||||
const options = fixture.debugElement.queryAll(By.css('[data-automation-class="dropdown-breadcrumb-path-option"]'));
|
||||
expect(path).not.toBeNull();
|
||||
expect(options.length).toBe(3);
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
openSelect();
|
||||
|
||||
const path = fixture.debugElement.query(By.css('[data-automation-id="dropdown-breadcrumb-path"]'));
|
||||
const options = fixture.debugElement.queryAll(By.css('[data-automation-class="dropdown-breadcrumb-path-option"]'));
|
||||
expect(path).not.toBeNull();
|
||||
expect(options.length).toBe(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display the path in reverse order', () => {
|
||||
fakeNodeWithCreatePermission.path.elements = [
|
||||
it('should display the path in reverse order', (done) => {
|
||||
let fakeNodeWithCreatePermissionInstance = JSON.parse(JSON.stringify(fakeNodeWithCreatePermission));
|
||||
fakeNodeWithCreatePermissionInstance.path.elements = [
|
||||
{ id: '1', name: 'Stark Industries' },
|
||||
{ id: '2', name: 'User Homes' },
|
||||
{ id: '3', name: 'J.A.R.V.I.S' }
|
||||
];
|
||||
|
||||
triggerComponentChange(fakeNodeWithCreatePermission);
|
||||
openSelect();
|
||||
triggerComponentChange(fakeNodeWithCreatePermissionInstance);
|
||||
|
||||
const options = fixture.debugElement.queryAll(By.css('[data-automation-class="dropdown-breadcrumb-path-option"]'));
|
||||
expect(options.length).toBe(3);
|
||||
expect(options[0].nativeElement.innerText.trim()).toBe('J.A.R.V.I.S');
|
||||
expect(options[1].nativeElement.innerText.trim()).toBe('User Homes');
|
||||
expect(options[2].nativeElement.innerText.trim()).toBe('Stark Industries');
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
openSelect();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
const options = fixture.debugElement.queryAll(By.css('[data-automation-class="dropdown-breadcrumb-path-option"]'));
|
||||
expect(options.length).toBe(3);
|
||||
expect(options[0].nativeElement.innerText.trim()).toBe('J.A.R.V.I.S');
|
||||
expect(options[1].nativeElement.innerText.trim()).toBe('User Homes');
|
||||
expect(options[2].nativeElement.innerText.trim()).toBe('Stark Industries');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit navigation event when clicking on an option', (done) => {
|
||||
fakeNodeWithCreatePermission.path.elements = [{ id: '1', name: 'Stark Industries' }];
|
||||
component.navigate.subscribe(val => {
|
||||
expect(val).toEqual({ id: '1', name: 'Stark Industries' });
|
||||
done();
|
||||
let fakeNodeWithCreatePermissionInstance = JSON.parse(JSON.stringify(fakeNodeWithCreatePermission));
|
||||
fakeNodeWithCreatePermissionInstance.path.elements = [{ id: '1', name: 'Stark Industries' }];
|
||||
|
||||
triggerComponentChange(fakeNodeWithCreatePermissionInstance);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
openSelect();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
component.navigate.subscribe(val => {
|
||||
expect(val).toEqual({ id: '1', name: 'Stark Industries' });
|
||||
done();
|
||||
});
|
||||
|
||||
clickOnTheFirstOption();
|
||||
});
|
||||
});
|
||||
|
||||
triggerComponentChange(fakeNodeWithCreatePermission);
|
||||
openSelect();
|
||||
|
||||
clickOnTheFirstOption();
|
||||
});
|
||||
|
||||
it('should update document list when clicking on an option', () => {
|
||||
it('should update document list when clicking on an option', (done) => {
|
||||
spyOn(documentList, 'loadFolderByNodeId').and.stub();
|
||||
component.target = documentList;
|
||||
fakeNodeWithCreatePermission.path.elements = [{ id: '1', name: 'Stark Industries' }];
|
||||
triggerComponentChange(fakeNodeWithCreatePermission);
|
||||
openSelect();
|
||||
let fakeNodeWithCreatePermissionInstance = JSON.parse(JSON.stringify(fakeNodeWithCreatePermission));
|
||||
fakeNodeWithCreatePermissionInstance.path.elements = [{ id: '1', name: 'Stark Industries' }];
|
||||
triggerComponentChange(fakeNodeWithCreatePermissionInstance);
|
||||
|
||||
clickOnTheFirstOption();
|
||||
fixture.whenStable().then(() => {
|
||||
openSelect();
|
||||
fixture.whenStable().then(() => {
|
||||
|
||||
expect(documentList.loadFolderByNodeId).toHaveBeenCalledWith('1');
|
||||
clickOnTheFirstOption();
|
||||
|
||||
expect(documentList.loadFolderByNodeId).toHaveBeenCalledWith('1');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should open the selectbox when clicking on the folder icon', async(() => {
|
||||
triggerComponentChange(fakeNodeWithCreatePermission);
|
||||
spyOn(component.selectbox, 'open');
|
||||
it('should open the selectbox when clicking on the folder icon', (done) => {
|
||||
triggerComponentChange(JSON.parse(JSON.stringify(fakeNodeWithCreatePermission)));
|
||||
spyOn(component.dropdown, 'open');
|
||||
|
||||
openSelect();
|
||||
fixture.whenStable().then(() => {
|
||||
openSelect();
|
||||
|
||||
expect(component.selectbox.open).toHaveBeenCalled();
|
||||
}));
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.dropdown.open).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import { Component, OnChanges, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { MatSelect } from '@angular/material';
|
||||
import { PathElementEntity } from 'alfresco-js-api';
|
||||
import { PathElementEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { BreadcrumbComponent } from './breadcrumb.component';
|
||||
|
||||
@Component({
|
||||
@@ -31,31 +31,40 @@ import { BreadcrumbComponent } from './breadcrumb.component';
|
||||
})
|
||||
export class DropdownBreadcrumbComponent extends BreadcrumbComponent implements OnChanges {
|
||||
|
||||
@ViewChild('select')
|
||||
selectbox: MatSelect;
|
||||
@ViewChild('dropdown')
|
||||
dropdown: MatSelect;
|
||||
|
||||
currentNode: PathElementEntity;
|
||||
previousNodes: PathElementEntity[];
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
super.ngOnChanges(changes);
|
||||
if (changes.folderNode) {
|
||||
let node: MinimalNodeEntryEntity = null;
|
||||
node = this.transform ? this.transform(changes.folderNode.currentValue) : changes.folderNode.currentValue;
|
||||
this.route = this.parseRoute(node);
|
||||
}
|
||||
|
||||
if (changes.transform) {
|
||||
let node = this.transform ? this.transform(this.folderNode) : this.folderNode;
|
||||
this.route = this.parseRoute(node);
|
||||
}
|
||||
this.recalculateNodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the current and previous nodes from the route array
|
||||
*/
|
||||
private recalculateNodes(): void {
|
||||
protected recalculateNodes(): void {
|
||||
this.currentNode = this.route[this.route.length - 1];
|
||||
this.previousNodes = this.route.slice(0, this.route.length - 1).reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the selectbox overlay
|
||||
* Opens the node picker menu
|
||||
*/
|
||||
open(): void {
|
||||
if (this.selectbox) {
|
||||
this.selectbox.open();
|
||||
if (this.dropdown) {
|
||||
this.dropdown.open();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
</mat-card-content>
|
||||
<mat-card-footer class="adf-content-metadata-card-footer" fxLayout="row" fxLayoutAlign="space-between stretch">
|
||||
<div>
|
||||
<button *ngIf="!readOnly"
|
||||
<button *ngIf="!readOnly && hasPermission()"
|
||||
mat-icon-button
|
||||
(click)="toggleEdit()"
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
|
||||
|
||||
+30
-38
@@ -17,46 +17,24 @@
|
||||
|
||||
/*tslint:disable: ban*/
|
||||
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { ContentMetadataCardComponent } from './content-metadata-card.component';
|
||||
import { ContentMetadataComponent } from '../content-metadata/content-metadata.component';
|
||||
import { MatExpansionModule, MatCardModule, MatButtonModule, MatIconModule } from '@angular/material';
|
||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||
import { BasicPropertiesService } from '../../services/basic-properties.service';
|
||||
import { PropertyGroupTranslatorService } from '../../services/property-groups-translator.service';
|
||||
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
||||
import { ContentMetadataConfigFactory } from '../../services/config/content-metadata-config.factory';
|
||||
import { setupTestBed, PermissionsEnum } from '@alfresco/adf-core';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('ContentMetadataCardComponent', () => {
|
||||
|
||||
let component: ContentMetadataCardComponent,
|
||||
fixture: ComponentFixture<ContentMetadataCardComponent>,
|
||||
node: MinimalNodeEntryEntity,
|
||||
preset = 'custom-preset';
|
||||
let component: ContentMetadataCardComponent;
|
||||
let fixture: ComponentFixture<ContentMetadataCardComponent>;
|
||||
let node: MinimalNodeEntryEntity;
|
||||
let preset = 'custom-preset';
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
MatExpansionModule,
|
||||
MatCardModule,
|
||||
MatButtonModule,
|
||||
MatIconModule
|
||||
],
|
||||
declarations: [
|
||||
ContentMetadataCardComponent,
|
||||
ContentMetadataComponent
|
||||
],
|
||||
providers: [
|
||||
ContentMetadataService,
|
||||
BasicPropertiesService,
|
||||
PropertyGroupTranslatorService,
|
||||
ContentMetadataConfigFactory,
|
||||
PropertyDescriptorsService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentMetadataCardComponent);
|
||||
@@ -139,6 +117,7 @@ describe('ContentMetadataCardComponent', () => {
|
||||
|
||||
it('should toggle editable by clicking on the button', () => {
|
||||
component.editable = true;
|
||||
component.node.allowableOperations = [PermissionsEnum.UPDATE];
|
||||
fixture.detectChanges();
|
||||
|
||||
const button = fixture.debugElement.query(By.css('[data-automation-id="mata-data-card-toggle-edit"]'));
|
||||
@@ -177,16 +156,29 @@ describe('ContentMetadataCardComponent', () => {
|
||||
expect(buttonLabel.nativeElement.innerText.trim()).toBe('ADF_VIEWER.SIDEBAR.METADATA.LESS_INFORMATION');
|
||||
});
|
||||
|
||||
it('should show the edit button by default', () => {
|
||||
const button = fixture.debugElement.query(By.css('[data-automation-id="mata-data-card-toggle-edit"]'));
|
||||
expect(button).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should hode the edit button in readOnly is true', () => {
|
||||
it('should hide the edit button in readOnly is true', () => {
|
||||
component.readOnly = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const button = fixture.debugElement.query(By.css('[data-automation-id="mata-data-card-toggle-edit"]'));
|
||||
expect(button).toBeNull();
|
||||
});
|
||||
|
||||
it('should hide the edit button if node does not have `update` permissions', () => {
|
||||
component.readOnly = false;
|
||||
component.node.allowableOperations = null;
|
||||
fixture.detectChanges();
|
||||
|
||||
const button = fixture.debugElement.query(By.css('[data-automation-id="mata-data-card-toggle-edit"]'));
|
||||
expect(button).toBeNull();
|
||||
});
|
||||
|
||||
it('should show the edit button if node does has `update` permissions', () => {
|
||||
component.readOnly = false;
|
||||
component.node.allowableOperations = [PermissionsEnum.UPDATE];
|
||||
fixture.detectChanges();
|
||||
|
||||
const button = fixture.debugElement.query(By.css('[data-automation-id="mata-data-card-toggle-edit"]'));
|
||||
expect(button).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
+8
@@ -17,6 +17,7 @@
|
||||
|
||||
import { Component, Input, ViewEncapsulation } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { ContentService, PermissionsEnum } from '@alfresco/adf-core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-metadata-card',
|
||||
@@ -44,6 +45,9 @@ export class ContentMetadataCardComponent {
|
||||
editable: boolean = false;
|
||||
expanded: boolean = false;
|
||||
|
||||
constructor(private contentService: ContentService) {
|
||||
}
|
||||
|
||||
toggleEdit(): void {
|
||||
this.editable = !this.editable;
|
||||
}
|
||||
@@ -51,4 +55,8 @@ export class ContentMetadataCardComponent {
|
||||
toggleExpanded(): void {
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
|
||||
hasPermission() {
|
||||
return this.contentService.hasPermission(this.node, PermissionsEnum.UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-64
@@ -22,51 +22,23 @@ import { SimpleChange } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { ContentMetadataComponent } from './content-metadata.component';
|
||||
import { MatExpansionModule, MatButtonModule, MatIconModule } from '@angular/material';
|
||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||
import { BasicPropertiesService } from '../../services/basic-properties.service';
|
||||
import { PropertyGroupTranslatorService } from '../../services/property-groups-translator.service';
|
||||
import { PropertyDescriptorsService } from '../../services/property-descriptors.service';
|
||||
import {
|
||||
CardViewBaseItemModel,
|
||||
CardViewComponent,
|
||||
CardViewUpdateService,
|
||||
NodesApiService,
|
||||
LogService
|
||||
} from '@alfresco/adf-core';
|
||||
import { CardViewBaseItemModel, CardViewComponent, CardViewUpdateService, NodesApiService, LogService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ContentMetadataConfigFactory } from '../../services/config/content-metadata-config.factory';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('ContentMetadataComponent', () => {
|
||||
let component: ContentMetadataComponent;
|
||||
let fixture: ComponentFixture<ContentMetadataComponent>;
|
||||
let node: MinimalNodeEntryEntity;
|
||||
let folderNode: MinimalNodeEntryEntity;
|
||||
let preset = 'custom-preset';
|
||||
|
||||
let component: ContentMetadataComponent,
|
||||
fixture: ComponentFixture<ContentMetadataComponent>,
|
||||
node: MinimalNodeEntryEntity,
|
||||
folderNode: MinimalNodeEntryEntity,
|
||||
preset = 'custom-preset';
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
MatExpansionModule,
|
||||
MatButtonModule,
|
||||
MatIconModule
|
||||
],
|
||||
declarations: [
|
||||
ContentMetadataComponent
|
||||
],
|
||||
providers: [
|
||||
ContentMetadataService,
|
||||
BasicPropertiesService,
|
||||
PropertyGroupTranslatorService,
|
||||
PropertyDescriptorsService,
|
||||
ContentMetadataConfigFactory,
|
||||
NodesApiService,
|
||||
{ provide: LogService, useValue: { error: jasmine.createSpy('error') } }
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
providers: [{ provide: LogService, useValue: { error: jasmine.createSpy('error') } }]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentMetadataComponent);
|
||||
@@ -96,11 +68,9 @@ describe('ContentMetadataComponent', () => {
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
describe('Default input param values', () => {
|
||||
|
||||
it('should have editable input param as false by default', () => {
|
||||
expect(component.editable).toBe(false);
|
||||
});
|
||||
@@ -115,7 +85,6 @@ describe('ContentMetadataComponent', () => {
|
||||
});
|
||||
|
||||
describe('Folder', () => {
|
||||
|
||||
it('should show the folder node', () => {
|
||||
component.expanded = false;
|
||||
fixture.detectChanges();
|
||||
@@ -131,7 +100,6 @@ describe('ContentMetadataComponent', () => {
|
||||
});
|
||||
|
||||
describe('Saving', () => {
|
||||
|
||||
it('should save the node on itemUpdate', () => {
|
||||
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' },
|
||||
updateService: CardViewUpdateService = fixture.debugElement.injector.get(CardViewUpdateService),
|
||||
@@ -158,7 +126,7 @@ describe('ContentMetadataComponent', () => {
|
||||
updateService.update(property, 'updated-value');
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.node).toBe(expectedNode);
|
||||
expect(component.node).toEqual(expectedNode);
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -179,9 +147,7 @@ describe('ContentMetadataComponent', () => {
|
||||
});
|
||||
|
||||
describe('Properties loading', () => {
|
||||
|
||||
let expectedNode,
|
||||
contentMetadataService: ContentMetadataService;
|
||||
let expectedNode, contentMetadataService: ContentMetadataService;
|
||||
|
||||
beforeEach(() => {
|
||||
expectedNode = Object.assign({}, node, { name: 'some-modified-value' });
|
||||
@@ -267,22 +233,5 @@ describe('ContentMetadataComponent', () => {
|
||||
expect(basicPropertiesComponent.displayEmpty).toBe(false);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should be performed again if property updating occured, since the originally passed node has changed, so the previously calculated properties', () => {
|
||||
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' },
|
||||
updateService = fixture.debugElement.injector.get(CardViewUpdateService),
|
||||
nodesApiService = TestBed.get(NodesApiService);
|
||||
|
||||
spyOn(nodesApiService, 'updateNode').and.callFake(() => Observable.of(node));
|
||||
spyOn(contentMetadataService, 'getBasicProperties');
|
||||
component.ngOnChanges({ node: new SimpleChange(null, node, true) });
|
||||
updateService.update(property, 'updated-value');
|
||||
|
||||
component.ngOnChanges({ expanded: new SimpleChange(false, true, false) });
|
||||
component.ngOnChanges({ expanded: new SimpleChange(true, false, false) });
|
||||
component.ngOnChanges({ expanded: new SimpleChange(false, true, false) });
|
||||
|
||||
expect(contentMetadataService.getBasicProperties).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+37
-19
@@ -15,12 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Input, OnChanges, OnInit, SimpleChanges, SimpleChange, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { CardViewItem, NodesApiService, LogService, CardViewUpdateService, AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { ContentMetadataService } from '../../services/content-metadata.service';
|
||||
import { CardViewGroup } from '../../interfaces/content-metadata.interfaces';
|
||||
import { Subscription } from 'rxjs/Rx';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-content-metadata',
|
||||
@@ -29,61 +30,78 @@ import { CardViewGroup } from '../../interfaces/content-metadata.interfaces';
|
||||
host: { 'class': 'adf-content-metadata' },
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class ContentMetadataComponent implements OnChanges, OnInit {
|
||||
|
||||
export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
|
||||
/** (required) The node entity to fetch metadata about */
|
||||
@Input()
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
/** Toggles whether the edit button should be shown */
|
||||
@Input()
|
||||
editable: boolean = false;
|
||||
|
||||
/** Toggles whether to display empty values in the card view */
|
||||
@Input()
|
||||
displayEmpty: boolean = false;
|
||||
|
||||
/** Toggles between expanded (ie, full information) and collapsed
|
||||
* (ie, reduced information) in the display
|
||||
*/
|
||||
@Input()
|
||||
expanded: boolean = false;
|
||||
|
||||
/** The multi parameter of the underlying material expansion panel */
|
||||
@Input()
|
||||
multi = false;
|
||||
|
||||
/** Name of the metadata preset, which defines aspects and their properties */
|
||||
@Input()
|
||||
preset: string;
|
||||
|
||||
nodeHasBeenUpdated: boolean = false;
|
||||
basicProperties$: Observable<CardViewItem[]>;
|
||||
groupedProperties$: Observable<CardViewGroup[]>;
|
||||
disposableNodeUpdate: Subscription;
|
||||
|
||||
constructor(private contentMetadataService: ContentMetadataService,
|
||||
private cardViewUpdateService: CardViewUpdateService,
|
||||
private nodesApi: NodesApiService,
|
||||
private logService: LogService,
|
||||
private apiService: AlfrescoApiService) {}
|
||||
constructor(
|
||||
private contentMetadataService: ContentMetadataService,
|
||||
private cardViewUpdateService: CardViewUpdateService,
|
||||
private nodesApiService: NodesApiService,
|
||||
private logService: LogService,
|
||||
private alfrescoApiService: AlfrescoApiService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.cardViewUpdateService.itemUpdated$
|
||||
this.disposableNodeUpdate = this.cardViewUpdateService.itemUpdated$
|
||||
.switchMap(this.saveNode.bind(this))
|
||||
.subscribe(
|
||||
(node) => {
|
||||
this.nodeHasBeenUpdated = true;
|
||||
this.node = node;
|
||||
this.apiService.nodeUpdated.next(node);
|
||||
updatedNode => {
|
||||
Object.assign(this.node, updatedNode);
|
||||
this.alfrescoApiService.nodeUpdated.next(this.node);
|
||||
},
|
||||
error => this.logService.error(error)
|
||||
);
|
||||
|
||||
this.loadProperties(this.node);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const nodeChange: SimpleChange = changes['node'];
|
||||
if (nodeChange || this.nodeHasBeenUpdated) {
|
||||
const node = nodeChange && nodeChange.currentValue || this.node;
|
||||
this.nodeHasBeenUpdated = false;
|
||||
if (changes.node && !changes.node.firstChange) {
|
||||
this.loadProperties(changes.node.currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
private loadProperties(node: MinimalNodeEntryEntity) {
|
||||
if (node) {
|
||||
this.basicProperties$ = this.contentMetadataService.getBasicProperties(node);
|
||||
this.groupedProperties$ = this.contentMetadataService.getGroupedProperties(node, this.preset);
|
||||
}
|
||||
}
|
||||
|
||||
private saveNode({ changed: nodeBody }): Observable<MinimalNodeEntryEntity> {
|
||||
return this.nodesApi.updateNode(this.node.id, nodeBody);
|
||||
return this.nodesApiService.updateNode(this.node.id, nodeBody);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.disposableNodeUpdate.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+84
-70
@@ -16,102 +16,116 @@
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { AppConfigService, LogService } from '@alfresco/adf-core';
|
||||
import { AppConfigService, LogService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { IndifferentConfigService } from './indifferent-config.service';
|
||||
import { AspectOrientedConfigService } from './aspect-oriented-config.service';
|
||||
import { LayoutOrientedConfigService } from './layout-oriented-config.service';
|
||||
import { ContentMetadataConfigFactory } from './content-metadata-config.factory';
|
||||
import { ContentMetadataConfig } from '../../interfaces/content-metadata.interfaces';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
describe('ContentMetadataConfigFactory', () => {
|
||||
|
||||
let factory: ContentMetadataConfigFactory,
|
||||
appConfig: AppConfigService,
|
||||
config: ContentMetadataConfig;
|
||||
let factory: ContentMetadataConfigFactory;
|
||||
let appConfig: AppConfigService;
|
||||
let config: ContentMetadataConfig;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
HttpClientModule
|
||||
],
|
||||
providers: [
|
||||
ContentMetadataConfigFactory,
|
||||
AppConfigService,
|
||||
{
|
||||
provide: LogService, useValue: {
|
||||
error: () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ContentMetadataConfigFactory,
|
||||
AppConfigService,
|
||||
{ provide: LogService, useValue: { error: () => {} }}
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
factory = TestBed.get(ContentMetadataConfigFactory);
|
||||
appConfig = TestBed.get(AppConfigService);
|
||||
});
|
||||
|
||||
function setConfig(presetName, presetConfig) {
|
||||
appConfig.config['content-metadata'] = {
|
||||
presets: {
|
||||
[presetName]: presetConfig
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('get', () => {
|
||||
|
||||
let logService;
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async(() => {
|
||||
logService = TestBed.get(LogService);
|
||||
spyOn(logService, 'error').and.stub();
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
it('should get back to default preset if no preset is provided as parameter', () => {
|
||||
config = factory.get();
|
||||
describe('get', () => {
|
||||
|
||||
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||
it('should get back to default preset if no preset is provided as parameter', async(() => {
|
||||
config = factory.get();
|
||||
|
||||
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||
}));
|
||||
|
||||
it('should get back to default preset if no preset is set', async(() => {
|
||||
config = factory.get('default');
|
||||
|
||||
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||
expect(logService.error).not.toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should get back to the default preset if the requested preset does not exist', async(() => {
|
||||
config = factory.get('not-existing-preset');
|
||||
|
||||
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||
}));
|
||||
|
||||
it('should log an error message if the requested preset does not exist', async(() => {
|
||||
config = factory.get('not-existing-preset');
|
||||
|
||||
expect(logService.error).toHaveBeenCalledWith('No content-metadata preset for: not-existing-preset');
|
||||
}));
|
||||
});
|
||||
|
||||
it('should get back to default preset if no preset is set', () => {
|
||||
config = factory.get('default');
|
||||
xdescribe('set', () => {
|
||||
|
||||
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||
expect(logService.error).not.toHaveBeenCalled();
|
||||
function setConfig(presetName, presetConfig) {
|
||||
appConfig.config['content-metadata'] = {
|
||||
presets: {
|
||||
[presetName]: presetConfig
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it('should get back the IndifferentConfigService preset if the preset config is indifferent', async(() => {
|
||||
setConfig('default', '*');
|
||||
|
||||
config = factory.get('default');
|
||||
|
||||
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||
}));
|
||||
|
||||
it('should get back the AspectOrientedConfigService preset if the preset config is aspect oriented', async(() => {
|
||||
setConfig('default', { 'exif:exif': '*' });
|
||||
|
||||
config = factory.get('default');
|
||||
|
||||
expect(config).toEqual(jasmine.any(AspectOrientedConfigService));
|
||||
}));
|
||||
|
||||
it('should get back the LayoutOrientedConfigService preset if the preset config is layout oriented', async(() => {
|
||||
setConfig('default', []);
|
||||
|
||||
config = factory.get('default');
|
||||
|
||||
expect(config).toEqual(jasmine.any(LayoutOrientedConfigService));
|
||||
}));
|
||||
});
|
||||
|
||||
it('should get back to the default preset if the requested preset does not exist', () => {
|
||||
config = factory.get('not-existing-preset');
|
||||
|
||||
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||
});
|
||||
|
||||
it('should log an error message if the requested preset does not exist', () => {
|
||||
config = factory.get('not-existing-preset');
|
||||
|
||||
expect(logService.error).toHaveBeenCalledWith('No content-metadata preset for: not-existing-preset');
|
||||
});
|
||||
|
||||
it('should get back the IndifferentConfigService preset if the preset config is indifferent', () => {
|
||||
setConfig('default', '*');
|
||||
|
||||
config = factory.get('default');
|
||||
|
||||
expect(config).toEqual(jasmine.any(IndifferentConfigService));
|
||||
});
|
||||
|
||||
it('should get back the AspectOrientedConfigService preset if the preset config is aspect oriented', () => {
|
||||
setConfig('default', { 'exif:exif' : '*'});
|
||||
|
||||
config = factory.get('default');
|
||||
|
||||
expect(config).toEqual(jasmine.any(AspectOrientedConfigService));
|
||||
});
|
||||
|
||||
it('should get back the LayoutOrientedConfigService preset if the preset config is layout oriented', () => {
|
||||
setConfig('default', []);
|
||||
|
||||
config = factory.get('default');
|
||||
|
||||
expect(config).toEqual(jasmine.any(LayoutOrientedConfigService));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+8
-15
@@ -15,25 +15,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { PropertyDescriptorsService } from './property-descriptors.service';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ClassesApi } from 'alfresco-js-api';
|
||||
import { PropertyGroup } from '../interfaces/content-metadata.interfaces';
|
||||
import { ContentTestingModule } from '../../testing/content.testing.module';
|
||||
|
||||
describe('PropertyDescriptorLoaderService', () => {
|
||||
|
||||
let service: PropertyDescriptorsService,
|
||||
classesApi: ClassesApi;
|
||||
let service: PropertyDescriptorsService;
|
||||
let classesApi: ClassesApi;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
PropertyDescriptorsService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.get(PropertyDescriptorsService);
|
||||
@@ -41,10 +38,6 @@ describe('PropertyDescriptorLoaderService', () => {
|
||||
classesApi = alfrescoApiService.classesApi;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
it('should load the groups passed by paramter', () => {
|
||||
spyOn(classesApi, 'getClass');
|
||||
|
||||
|
||||
+40
-25
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { PropertyGroupTranslatorService } from './property-groups-translator.service';
|
||||
import { Property, OrganisedPropertyGroup } from '../interfaces/content-metadata.interfaces';
|
||||
import {
|
||||
@@ -25,25 +25,30 @@ import {
|
||||
CardViewFloatItemModel,
|
||||
LogService,
|
||||
CardViewBoolItemModel,
|
||||
CardViewDatetimeItemModel
|
||||
CardViewDatetimeItemModel,
|
||||
setupTestBed
|
||||
} from '@alfresco/adf-core';
|
||||
import { ContentTestingModule } from '../../testing/content.testing.module';
|
||||
|
||||
describe('PropertyGroupTranslatorService', () => {
|
||||
|
||||
let service: PropertyGroupTranslatorService,
|
||||
propertyGroups: OrganisedPropertyGroup[],
|
||||
propertyGroup: OrganisedPropertyGroup,
|
||||
property: Property,
|
||||
propertyValues: { [key: string]: any };
|
||||
let service: PropertyGroupTranslatorService;
|
||||
let propertyGroups: OrganisedPropertyGroup[];
|
||||
let propertyGroup: OrganisedPropertyGroup;
|
||||
let property: Property;
|
||||
let propertyValues: { [key: string]: any };
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
PropertyGroupTranslatorService,
|
||||
{ provide: LogService, useValue: { error: () => {} }}
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: LogService, useValue: {
|
||||
error: () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.get(PropertyGroupTranslatorService);
|
||||
@@ -56,10 +61,12 @@ describe('PropertyGroupTranslatorService', () => {
|
||||
mandatory: false,
|
||||
multiValued: false
|
||||
};
|
||||
|
||||
propertyGroup = {
|
||||
title: 'Faro Automated Solutions',
|
||||
properties: [property]
|
||||
};
|
||||
|
||||
propertyGroups = [];
|
||||
});
|
||||
|
||||
@@ -78,14 +85,14 @@ describe('PropertyGroupTranslatorService', () => {
|
||||
mandatory: false,
|
||||
multiValued: false
|
||||
},
|
||||
{
|
||||
name: 'FAS:ALOY',
|
||||
title: 'title',
|
||||
dataType: 'd:text',
|
||||
defaultValue: 'defaultValue',
|
||||
mandatory: false,
|
||||
multiValued: false
|
||||
}];
|
||||
{
|
||||
name: 'FAS:ALOY',
|
||||
title: 'title',
|
||||
dataType: 'd:text',
|
||||
defaultValue: 'defaultValue',
|
||||
mandatory: false,
|
||||
multiValued: false
|
||||
}];
|
||||
propertyGroups.push(propertyGroup);
|
||||
|
||||
propertyValues = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||
@@ -136,7 +143,10 @@ describe('PropertyGroupTranslatorService', () => {
|
||||
property.title = 'The Faro Plague';
|
||||
property.dataType = 'daemonic:scorcher';
|
||||
property.defaultValue = 'Daemonic beast';
|
||||
propertyGroups.push(propertyGroup);
|
||||
|
||||
propertyValues = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||
|
||||
propertyGroups.push(Object.assign({}, propertyGroup));
|
||||
|
||||
service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||
expect(logService.error).toHaveBeenCalledWith('Unknown type for mapping: daemonic:scorcher');
|
||||
@@ -147,7 +157,12 @@ describe('PropertyGroupTranslatorService', () => {
|
||||
property.title = 'The Faro Plague';
|
||||
property.dataType = 'daemonic:scorcher';
|
||||
property.defaultValue = 'Daemonic beast';
|
||||
propertyGroups.push(propertyGroup);
|
||||
propertyGroups.push({
|
||||
title: 'Faro Automated Solutions',
|
||||
properties: [property]
|
||||
});
|
||||
|
||||
propertyValues = { 'FAS:PLAGUE': 'The Chariot Line' };
|
||||
|
||||
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues);
|
||||
const cardViewProperty: CardViewTextItemModel = <CardViewTextItemModel> cardViewGroup[0].properties[0];
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { MinimalNodeEntryEntity, SitePaging } from 'alfresco-js-api';
|
||||
import { AppConfigService, SitesService } from '@alfresco/adf-core';
|
||||
import { AppConfigService, SitesService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from '../document-list/services/document-list.service';
|
||||
import { CustomResourcesService } from '../document-list/services/custom-resources.service';
|
||||
import { ContentNodeDialogService } from './content-node-dialog.service';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
|
||||
const fakeNode: MinimalNodeEntryEntity = <MinimalNodeEntryEntity> {
|
||||
id: 'fake',
|
||||
@@ -60,18 +60,9 @@ describe('ContentNodeDialogService', () => {
|
||||
let spyOnDialogOpen: jasmine.Spy;
|
||||
let afterOpenObservable: Subject<any>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ContentNodeDialogService,
|
||||
DocumentListService,
|
||||
CustomResourcesService,
|
||||
SitesService,
|
||||
MatDialog
|
||||
]
|
||||
}).compileComponents();
|
||||
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
let appConfig: AppConfigService = TestBed.get(AppConfigService);
|
||||
@@ -89,7 +80,6 @@ describe('ContentNodeDialogService', () => {
|
||||
error: new Subject<any>()
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should not open the lock node dialog if have no permission', () => {
|
||||
|
||||
@@ -32,6 +32,7 @@ import 'rxjs/operator/switchMap';
|
||||
@Injectable()
|
||||
export class ContentNodeDialogService {
|
||||
|
||||
/** Emitted when an error occurs. */
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
@@ -42,8 +43,11 @@ export class ContentNodeDialogService {
|
||||
private translation: TranslationService) {
|
||||
}
|
||||
|
||||
/** Opens a file browser at a chosen folder location. */
|
||||
/** @param folderNodeId ID of the folder to use */
|
||||
/**
|
||||
* Opens a file browser at a chosen folder location.
|
||||
* @param folderNodeId ID of the folder to use
|
||||
* @returns Information about the selected file(s)
|
||||
*/
|
||||
openFileBrowseDialogByFolderId(folderNodeId: string): Observable<MinimalNodeEntryEntity[]> {
|
||||
return this.documentListService.getFolderNode(folderNodeId).switchMap((node: MinimalNodeEntryEntity) => {
|
||||
return this.openUploadFileDialog('Choose', node);
|
||||
@@ -51,9 +55,9 @@ export class ContentNodeDialogService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a lock node dialog
|
||||
*
|
||||
* Opens a lock node dialog.
|
||||
* @param contentEntry Node to lock
|
||||
* @returns Error/status message (if any)
|
||||
*/
|
||||
public openLockNodeDialog(contentEntry: MinimalNodeEntryEntity): Subject<string> {
|
||||
const observable: Subject<string> = new Subject<string>();
|
||||
@@ -76,33 +80,44 @@ export class ContentNodeDialogService {
|
||||
return observable;
|
||||
}
|
||||
|
||||
/** Opens a file browser at a chosen site location. */
|
||||
/**
|
||||
* Opens a file browser at a chosen site location.
|
||||
* @returns Information about the selected file(s)
|
||||
*/
|
||||
openFileBrowseDialogBySite(): Observable<MinimalNodeEntryEntity[]> {
|
||||
return this.siteService.getSites().switchMap((response: SitePaging) => {
|
||||
return this.openFileBrowseDialogByFolderId(response.list.entries[0].entry.guid);
|
||||
});
|
||||
}
|
||||
|
||||
/** Opens a folder browser at a chosen site location. */
|
||||
/**
|
||||
* Opens a folder browser at a chosen site location.
|
||||
* @returns Information about the selected folder(s)
|
||||
*/
|
||||
openFolderBrowseDialogBySite(): Observable<MinimalNodeEntryEntity[]> {
|
||||
return this.siteService.getSites().switchMap((response: SitePaging) => {
|
||||
return this.openFolderBrowseDialogByFolderId(response.list.entries[0].entry.guid);
|
||||
});
|
||||
}
|
||||
|
||||
/** Opens a folder browser at a chosen folder location. */
|
||||
/** @param folderNodeId ID of the folder to use */
|
||||
/**
|
||||
* Opens a folder browser at a chosen folder location.
|
||||
* @param folderNodeId ID of the folder to use
|
||||
* @returns Information about the selected folder(s)
|
||||
*/
|
||||
openFolderBrowseDialogByFolderId(folderNodeId: string): Observable<MinimalNodeEntryEntity[]> {
|
||||
return this.documentListService.getFolderNode(folderNodeId).switchMap((node: MinimalNodeEntryEntity) => {
|
||||
return this.openUploadFolderDialog('Choose', node);
|
||||
});
|
||||
}
|
||||
|
||||
/** Opens a dialog to copy or move an item to a new location. */
|
||||
/** @param action Name of the action (eg, "Copy" or "Move") to show in the title */
|
||||
|
||||
/** @param contentEntry Item to be copied or moved */
|
||||
/** @param permission Permission for the operation */
|
||||
/**
|
||||
* Opens a dialog to copy or move an item to a new location.
|
||||
* @param action Name of the action (eg, "Copy" or "Move") to show in the title
|
||||
* @param contentEntry Item to be copied or moved
|
||||
* @param permission Permission for the operation
|
||||
* @returns Information about files that were copied/moved
|
||||
*/
|
||||
openCopyMoveDialog(action: string, contentEntry: MinimalNodeEntryEntity, permission?: string): Observable<MinimalNodeEntryEntity[]> {
|
||||
if (this.contentService.hasPermission(contentEntry, permission)) {
|
||||
|
||||
@@ -132,18 +147,22 @@ export class ContentNodeDialogService {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the translation of the dialog title. */
|
||||
|
||||
/** @param action Name of the action to display in the dialog title */
|
||||
/** @param name Name of the item on which the action is being performed */
|
||||
/**
|
||||
* Gets the translation of the dialog title.
|
||||
* @param action Name of the action to display in the dialog title
|
||||
* @param name Name of the item on which the action is being performed
|
||||
* @returns Translated version of the title
|
||||
*/
|
||||
getTitleTranslation(action: string, name: string): string {
|
||||
return this.translation.instant(`NODE_SELECTOR.${action.toUpperCase()}_ITEM`, { name });
|
||||
}
|
||||
|
||||
/** Opens a dialog to choose a folder to upload. */
|
||||
|
||||
/** @param action Name of the action to show in the title */
|
||||
/** @param contentEntry Item to upload */
|
||||
/**
|
||||
* Opens a dialog to choose folders to upload.
|
||||
* @param action Name of the action to show in the title
|
||||
* @param contentEntry Item to upload
|
||||
* @returns Information about the chosed folder(s)
|
||||
*/
|
||||
openUploadFolderDialog(action: string, contentEntry: MinimalNodeEntryEntity): Observable<MinimalNodeEntryEntity[]> {
|
||||
const select = new Subject<MinimalNodeEntryEntity[]>();
|
||||
select.subscribe({
|
||||
@@ -164,10 +183,12 @@ export class ContentNodeDialogService {
|
||||
return select;
|
||||
}
|
||||
|
||||
/** Opens a dialog to choose a file to upload. */
|
||||
|
||||
/** @param action Name of the action to show in the title */
|
||||
/** @param contentEntry Item to upload */
|
||||
/**
|
||||
* Opens a dialog to choose a file to upload.
|
||||
* @param action Name of the action to show in the title
|
||||
* @param contentEntry Item to upload
|
||||
* @returns Information about the chosen file(s)
|
||||
*/
|
||||
openUploadFileDialog(action: string, contentEntry: MinimalNodeEntryEntity): Observable<MinimalNodeEntryEntity[]> {
|
||||
const select = new Subject<MinimalNodeEntryEntity[]>();
|
||||
select.subscribe({
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@
|
||||
}
|
||||
|
||||
&-breadcrumb {
|
||||
.adf-dropdown-breadcumb-trigger {
|
||||
.adf-dropdown-breadcrumb-trigger {
|
||||
outline: none;
|
||||
.mat-icon {
|
||||
color: mat-color($foreground, base, 0.45);
|
||||
|
||||
+12
-37
@@ -19,21 +19,16 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { async, fakeAsync, tick, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MinimalNodeEntryEntity, SiteEntry, SitePaging } from 'alfresco-js-api';
|
||||
import { SearchService, SitesService } from '@alfresco/adf-core';
|
||||
import { DataTableModule } from '@alfresco/adf-core';
|
||||
import { SearchService, SitesService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Observer } from 'rxjs/Observer';
|
||||
import {
|
||||
CustomResourcesService,
|
||||
EmptyFolderContentDirective,
|
||||
DocumentListComponent,
|
||||
DocumentListService
|
||||
} from '../document-list';
|
||||
import { DropdownSitesComponent } from '../site-dropdown';
|
||||
import { DropdownBreadcrumbComponent } from '../breadcrumb';
|
||||
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
|
||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
||||
import { NodePaging } from 'alfresco-js-api';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
import { DocumentListService } from '../document-list/services/document-list.service';
|
||||
import { DocumentListComponent } from '../document-list/components/document-list.component';
|
||||
|
||||
const ONE_FOLDER_RESULT = {
|
||||
list: {
|
||||
@@ -74,37 +69,13 @@ describe('ContentNodeSelectorComponent', () => {
|
||||
_observer.next(result);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
TestBed.resetTestingModule();
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
describe('General component features', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent,
|
||||
EmptyFolderContentDirective,
|
||||
DropdownSitesComponent,
|
||||
DropdownBreadcrumbComponent,
|
||||
ContentNodeSelectorPanelComponent
|
||||
],
|
||||
providers: [
|
||||
CustomResourcesService,
|
||||
SearchService,
|
||||
DocumentListService,
|
||||
SitesService,
|
||||
ContentNodeSelectorService
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentNodeSelectorPanelComponent);
|
||||
component = fixture.componentInstance;
|
||||
@@ -120,6 +91,10 @@ describe('ContentNodeSelectorComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
describe('Parameters', () => {
|
||||
|
||||
it('should trigger the select event when selection has been made', (done) => {
|
||||
@@ -518,7 +493,7 @@ describe('ContentNodeSelectorComponent', () => {
|
||||
|
||||
expect(component.searchTerm).toBe('');
|
||||
expect(component.folderIdToShow).toBe('namek');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should show the current folder\'s content instead of search results if search was not performed', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
|
||||
+48
-62
@@ -18,74 +18,53 @@
|
||||
import { MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { EventEmitter } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ContentNodeSelectorComponent } from './content-node-selector.component';
|
||||
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
|
||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import {
|
||||
EmptyFolderContentDirective,
|
||||
DocumentListComponent,
|
||||
DocumentListService,
|
||||
CustomResourcesService
|
||||
} from '../document-list';
|
||||
import { ContentService } from '@alfresco/adf-core';
|
||||
import { setupTestBed, SitesService } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
import { DocumentListService } from '../document-list/services/document-list.service';
|
||||
import { DocumentListComponent } from '../document-list/components/document-list.component';
|
||||
|
||||
describe('ContentNodeSelectorDialogComponent', () => {
|
||||
|
||||
let component: ContentNodeSelectorComponent;
|
||||
let fixture: ComponentFixture<ContentNodeSelectorComponent>;
|
||||
let data: any;
|
||||
let data: any = {
|
||||
title: 'Move along citizen...',
|
||||
actionName: 'move',
|
||||
select: new EventEmitter<MinimalNodeEntryEntity>(),
|
||||
rowFilter: () => {
|
||||
},
|
||||
imageResolver: () => 'piccolo',
|
||||
currentFolderId: 'cat-girl-nuku-nuku'
|
||||
};
|
||||
|
||||
function setupTestbed(plusProviders) {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
],
|
||||
declarations: [
|
||||
ContentNodeSelectorComponent,
|
||||
ContentNodeSelectorPanelComponent,
|
||||
DocumentListComponent,
|
||||
EmptyFolderContentDirective
|
||||
],
|
||||
providers: [
|
||||
CustomResourcesService,
|
||||
ContentNodeSelectorService,
|
||||
ContentNodeSelectorPanelComponent,
|
||||
DocumentListService,
|
||||
ContentService,
|
||||
DocumentListService,
|
||||
ContentNodeSelectorService,
|
||||
...plusProviders
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
}
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
providers: [
|
||||
{ provide: MAT_DIALOG_DATA, useValue: data }
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const documentListService: DocumentListService = TestBed.get(DocumentListService);
|
||||
const sitesService: SitesService = TestBed.get(SitesService);
|
||||
spyOn(documentListService, 'getFolder').and.returnValue(Observable.of({ list: [] }));
|
||||
spyOn(documentListService, 'getFolderNode').and.returnValue(Observable.of({}));
|
||||
spyOn(sitesService, 'getSites').and.returnValue(Observable.of({ list: { entries: [] } }));
|
||||
|
||||
fixture = TestBed.createComponent(ContentNodeSelectorComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
data = {
|
||||
title: 'Move along citizen...',
|
||||
actionName: 'move',
|
||||
select: new EventEmitter<MinimalNodeEntryEntity>(),
|
||||
rowFilter: () => {
|
||||
},
|
||||
imageResolver: () => 'piccolo',
|
||||
currentFolderId: 'cat-girl-nuku-nuku'
|
||||
};
|
||||
|
||||
setupTestbed([{ provide: MAT_DIALOG_DATA, useValue: data }]);
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContentNodeSelectorComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('Data injecting with the "Material dialog way"', () => {
|
||||
@@ -98,6 +77,7 @@ describe('ContentNodeSelectorDialogComponent', () => {
|
||||
|
||||
it('should have the INJECTED actionName on the name of the choose button', () => {
|
||||
const actionButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
|
||||
expect(component.buttonActionName).toBe('NODE_SELECTOR.MOVE');
|
||||
expect(actionButton).not.toBeNull();
|
||||
expect(actionButton.nativeElement.innerText).toBe('NODE_SELECTOR.MOVE');
|
||||
});
|
||||
@@ -108,10 +88,14 @@ describe('ContentNodeSelectorDialogComponent', () => {
|
||||
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
|
||||
});
|
||||
|
||||
it('should pass through the injected rowFilter to the documentlist', () => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.rowFilter).toBe(data.rowFilter);
|
||||
it('should pass through the injected rowFilter to the documentlist', (done) => {
|
||||
fixture.whenStable().then(() => {
|
||||
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
|
||||
expect(documentList).not.toBeNull('Document list should be shown');
|
||||
expect(documentList.componentInstance.rowFilter).toBe(data.rowFilter);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should pass through the injected imageResolver to the documentlist', () => {
|
||||
@@ -127,8 +111,10 @@ describe('ContentNodeSelectorDialogComponent', () => {
|
||||
it('should complete the data stream when user click "CANCEL"', () => {
|
||||
let cancelButton;
|
||||
data.select.subscribe(
|
||||
() => { },
|
||||
() => { },
|
||||
() => {
|
||||
},
|
||||
() => {
|
||||
},
|
||||
() => {
|
||||
cancelButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-cancel"]'));
|
||||
expect(cancelButton).not.toBeNull();
|
||||
|
||||
@@ -15,10 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { QueryBody } from 'alfresco-js-api';
|
||||
import { SearchService } from '@alfresco/adf-core';
|
||||
import { SearchService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { ContentNodeSelectorService } from './content-node-selector.service';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
|
||||
class SearchServiceMock {
|
||||
public query: QueryBody;
|
||||
@@ -29,17 +30,15 @@ class SearchServiceMock {
|
||||
|
||||
describe('ContentNodeSelectorService', () => {
|
||||
|
||||
let service: ContentNodeSelectorService,
|
||||
search: SearchServiceMock;
|
||||
let service: ContentNodeSelectorService;
|
||||
let search: SearchServiceMock;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ContentNodeSelectorService,
|
||||
{ provide: SearchService, useClass: SearchServiceMock }
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
providers: [
|
||||
{ provide: SearchService, useClass: SearchServiceMock }
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.get(ContentNodeSelectorService);
|
||||
|
||||
@@ -44,6 +44,7 @@ import { ContentMetadataConfigFactory } from './content-metadata/services/config
|
||||
import { BasicPropertiesService } from './content-metadata/services/basic-properties.service';
|
||||
import { PropertyGroupTranslatorService } from './content-metadata/services/property-groups-translator.service';
|
||||
import { SearchQueryBuilderService } from './search/search-query-builder.service';
|
||||
import { SearchFilterService } from './search/components/search-filter/search-filter.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -83,7 +84,8 @@ import { SearchQueryBuilderService } from './search/search-query-builder.service
|
||||
ContentMetadataConfigFactory,
|
||||
BasicPropertiesService,
|
||||
PropertyGroupTranslatorService,
|
||||
SearchQueryBuilderService
|
||||
SearchQueryBuilderService,
|
||||
SearchFilterService
|
||||
],
|
||||
exports: [
|
||||
CoreModule,
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
<h2 mat-dialog-title>
|
||||
{{
|
||||
(editing
|
||||
? 'CORE.FOLDER_DIALOG.EDIT_FOLDER_TITLE'
|
||||
: 'CORE.FOLDER_DIALOG.CREATE_FOLDER_TITLE'
|
||||
) | translate
|
||||
}}
|
||||
{{ (editing ? editTitle : createTitle) | translate }}
|
||||
</h2>
|
||||
|
||||
<mat-dialog-content>
|
||||
|
||||
@@ -6,6 +6,22 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.adf-lock-file-name {
|
||||
.mat-checkbox-layout{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-checkbox-label{
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mat-checkbox-inner-container{
|
||||
margin: auto 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin adf-dialog-theme($theme) {
|
||||
|
||||
$primary: map-get($theme, primary);
|
||||
|
||||
@@ -15,60 +15,40 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { async, ComponentFixture } from '@angular/core/testing';
|
||||
import { MatDialogRef } from '@angular/material';
|
||||
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { NodesApiService, TranslationService } from '@alfresco/adf-core';
|
||||
import { NodesApiService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { FolderDialogComponent } from './folder.dialog';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('FolderDialogComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<FolderDialogComponent>;
|
||||
let component: FolderDialogComponent;
|
||||
let translationService: TranslationService;
|
||||
let nodesApi: NodesApiService;
|
||||
let dialogRef;
|
||||
let dialogRef = {
|
||||
close: jasmine.createSpy('close')
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
dialogRef = {
|
||||
close: jasmine.createSpy('close')
|
||||
};
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
BrowserDynamicTestingModule
|
||||
],
|
||||
declarations: [
|
||||
FolderDialogComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef }
|
||||
]
|
||||
});
|
||||
|
||||
// entryComponents are not supported yet on TestBed, that is why this ugly workaround:
|
||||
// https://github.com/angular/angular/issues/10760
|
||||
TestBed.overrideModule(BrowserDynamicTestingModule, {
|
||||
set: { entryComponents: [FolderDialogComponent] }
|
||||
});
|
||||
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef }
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
dialogRef.close.calls.reset();
|
||||
fixture = TestBed.createComponent(FolderDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
nodesApi = TestBed.get(NodesApiService);
|
||||
});
|
||||
|
||||
translationService = TestBed.get(TranslationService);
|
||||
spyOn(translationService, 'get').and.returnValue(Observable.of('message'));
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
describe('Edit', () => {
|
||||
@@ -91,6 +71,12 @@ describe('FolderDialogComponent', () => {
|
||||
expect(component.description).toBe('folder-description');
|
||||
});
|
||||
|
||||
it('should have the proper title', () => {
|
||||
const title = fixture.debugElement.query(By.css('[mat-dialog-title]'));
|
||||
expect(title === null).toBe(false);
|
||||
expect(title.nativeElement.innerText.trim()).toBe('CORE.FOLDER_DIALOG.EDIT_FOLDER_TITLE');
|
||||
});
|
||||
|
||||
it('should update form input', () => {
|
||||
component.form.controls['name'].setValue('folder-name-update');
|
||||
component.form.controls['description'].setValue('folder-description-update');
|
||||
@@ -119,7 +105,7 @@ describe('FolderDialogComponent', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should call dialog to close with form data when submit is succesfluly', () => {
|
||||
it('should call dialog to close with form data when submit is successfully', () => {
|
||||
const folder = {
|
||||
data: 'folder-data'
|
||||
};
|
||||
@@ -131,6 +117,20 @@ describe('FolderDialogComponent', () => {
|
||||
expect(dialogRef.close).toHaveBeenCalledWith(folder);
|
||||
});
|
||||
|
||||
it('should emit success output event with folder when submit is succesfull', async(() => {
|
||||
const folder = { data: 'folder-data' };
|
||||
let expectedNode = null;
|
||||
|
||||
spyOn(nodesApi, 'updateNode').and.returnValue(Observable.of(folder));
|
||||
|
||||
component.success.subscribe((node) => { expectedNode = node; });
|
||||
component.submit();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(expectedNode).toBe(folder);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not submit if form is invalid', () => {
|
||||
spyOn(nodesApi, 'updateNode');
|
||||
|
||||
@@ -163,6 +163,12 @@ describe('FolderDialogComponent', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should have the proper title', () => {
|
||||
const title = fixture.debugElement.query(By.css('[mat-dialog-title]'));
|
||||
expect(title === null).toBe(false);
|
||||
expect(title.nativeElement.innerText.trim()).toBe('CORE.FOLDER_DIALOG.CREATE_FOLDER_TITLE');
|
||||
});
|
||||
|
||||
it('should init form with empty inputs', () => {
|
||||
expect(component.name).toBe('');
|
||||
expect(component.description).toBe('');
|
||||
@@ -191,12 +197,35 @@ describe('FolderDialogComponent', () => {
|
||||
properties: {
|
||||
'cm:title': 'folder-name-update',
|
||||
'cm:description': 'folder-description-update'
|
||||
}
|
||||
},
|
||||
nodeType: 'cm:folder'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should call dialog to close with form data when submit is succesfluly', () => {
|
||||
it('should submit updated values if form is valid (with custom nodeType)', () => {
|
||||
spyOn(nodesApi, 'createFolder').and.returnValue(Observable.of({}));
|
||||
|
||||
component.form.controls['name'].setValue('folder-name-update');
|
||||
component.form.controls['description'].setValue('folder-description-update');
|
||||
component.nodeType = 'cm:sushi';
|
||||
|
||||
component.submit();
|
||||
|
||||
expect(nodesApi.createFolder).toHaveBeenCalledWith(
|
||||
'parentNodeId',
|
||||
{
|
||||
name: 'folder-name-update',
|
||||
properties: {
|
||||
'cm:title': 'folder-name-update',
|
||||
'cm:description': 'folder-description-update'
|
||||
},
|
||||
nodeType: 'cm:sushi'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should call dialog to close with form data when submit is successfully', () => {
|
||||
const folder = {
|
||||
data: 'folder-data'
|
||||
};
|
||||
|
||||
@@ -42,6 +42,15 @@ export class FolderDialogComponent implements OnInit {
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
/** Emitted when the edit/create folder is successfully created/mmodified
|
||||
*/
|
||||
@Output()
|
||||
success: EventEmitter<any> = new EventEmitter<MinimalNodeEntryEntity>();
|
||||
|
||||
editTitle = 'CORE.FOLDER_DIALOG.EDIT_FOLDER_TITLE';
|
||||
createTitle = 'CORE.FOLDER_DIALOG.CREATE_FOLDER_TITLE';
|
||||
nodeType = 'cm:folder';
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private dialog: MatDialogRef<FolderDialogComponent>,
|
||||
@@ -50,7 +59,13 @@ export class FolderDialogComponent implements OnInit {
|
||||
@Optional()
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
public data: any
|
||||
) {}
|
||||
) {
|
||||
if (data) {
|
||||
this.editTitle = data.editTitle || this.editTitle;
|
||||
this.createTitle = data.createTitle || this.createTitle;
|
||||
this.nodeType = data.nodeType || this.nodeType;
|
||||
}
|
||||
}
|
||||
|
||||
get editing(): boolean {
|
||||
return !!this.data.folder;
|
||||
@@ -105,8 +120,8 @@ export class FolderDialogComponent implements OnInit {
|
||||
}
|
||||
|
||||
private create(): Observable<MinimalNodeEntryEntity> {
|
||||
const { name, properties, nodesApi, data: { parentNodeId} } = this;
|
||||
return nodesApi.createFolder(parentNodeId, { name, properties });
|
||||
const { name, properties, nodeType, nodesApi, data: { parentNodeId} } = this;
|
||||
return nodesApi.createFolder(parentNodeId, { name, properties, nodeType });
|
||||
}
|
||||
|
||||
private edit(): Observable<MinimalNodeEntryEntity> {
|
||||
@@ -121,7 +136,10 @@ export class FolderDialogComponent implements OnInit {
|
||||
|
||||
(editing ? this.edit() : this.create())
|
||||
.subscribe(
|
||||
(folder: MinimalNodeEntryEntity) => dialog.close(folder),
|
||||
(folder: MinimalNodeEntryEntity) => {
|
||||
this.success.emit(folder);
|
||||
dialog.close(folder);
|
||||
},
|
||||
(error) => this.handleError(error)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
<mat-dialog-content>
|
||||
<br />
|
||||
<form [formGroup]="form" (submit)="submit()">
|
||||
<mat-checkbox [formControl]="form.controls['isLocked']" ngDefaultControl>
|
||||
<mat-checkbox class="adf-lock-file-name" [formControl]="form.controls['isLocked']" ngDefaultControl>
|
||||
{{ 'CORE.FILE_DIALOG.FILE_LOCK_CHECKBOX' | translate }} <strong>"{{ nodeName }}"</strong>
|
||||
</mat-checkbox>
|
||||
|
||||
<br />
|
||||
|
||||
<div *ngIf="form.value.isLocked">
|
||||
<mat-checkbox [formControl]="form.controls['allowOwner']" ngDefaultControl>
|
||||
<mat-checkbox class="adf-lock-file-name" [formControl]="form.controls['allowOwner']" ngDefaultControl>
|
||||
{{ 'CORE.FILE_DIALOG.ALLOW_OTHERS_CHECKBOX' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<br />
|
||||
|
||||
<mat-checkbox [formControl]="form.controls['isTimeLock']" ngDefaultControl>
|
||||
<mat-checkbox class="adf-lock-file-name" [formControl]="form.controls['isTimeLock']" ngDefaultControl>
|
||||
{{ 'CORE.FILE_DIALOG.TIME_LOCK_CHECKBOX' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
|
||||
@@ -17,57 +17,38 @@
|
||||
|
||||
import moment from 'moment-es6';
|
||||
|
||||
import { async, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { ComponentFixture } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatDialogRef } from '@angular/material';
|
||||
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { AlfrescoApiService, TranslationService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { NodeLockDialogComponent } from './node-lock.dialog';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
|
||||
describe('NodeLockDialogComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<NodeLockDialogComponent>;
|
||||
let component: NodeLockDialogComponent;
|
||||
let translationService: TranslationService;
|
||||
let alfrescoApi: AlfrescoApiService;
|
||||
let expiryDate;
|
||||
const dialogRef = {
|
||||
close: jasmine.createSpy('close')
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
BrowserDynamicTestingModule
|
||||
],
|
||||
declarations: [
|
||||
NodeLockDialogComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef }
|
||||
]
|
||||
});
|
||||
|
||||
TestBed.overrideModule(BrowserDynamicTestingModule, {
|
||||
set: { entryComponents: [NodeLockDialogComponent] }
|
||||
});
|
||||
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef }
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NodeLockDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
alfrescoApi = TestBed.get(AlfrescoApiService);
|
||||
});
|
||||
|
||||
translationService = TestBed.get(TranslationService);
|
||||
spyOn(translationService, 'get').and.returnValue(Observable.of('message'));
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
describe('Node lock dialog component', () => {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<h1 mat-dialog-title id="adf-share-title" >{{ 'SHARE.TITLE' | translate }} {{fileName}}</h1>
|
||||
<h1 mat-dialog-title id="adf-share-title">{{ 'SHARE.TITLE' | translate }} {{fileName}}</h1>
|
||||
|
||||
<mat-dialog-content>
|
||||
<span class="spacer"></span>
|
||||
|
||||
<mat-slide-toggle [checked]="isFileShared" [disabled]="isDisabled" (change)="onSlideShareChange($event)">
|
||||
<mat-slide-toggle id="adf-share-toggle" [checked]="isFileShared" [disabled]="isDisabled"
|
||||
(change)="onSlideShareChange($event)">
|
||||
{{ 'SHARE.ACTIONS.SHARE' | translate }}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<span class="spacer"></span>
|
||||
|
||||
.
|
||||
|
||||
<h2 *ngIf="isFileShared">{{ 'SHARE.DESCRIPTION' | translate }}</h2>
|
||||
<div *ngIf="isFileShared">{{ 'SHARE.ALERT' | translate }}</div>
|
||||
|
||||
<form *ngIf="isFileShared">
|
||||
<mat-form-field class="full-width">
|
||||
<input class="adf-share-link" matInput placeholder="{{ 'SHARE.PUBLIC-LINK' | translate }}" [value]="baseShareUrl + sharedId">
|
||||
<input id="adf-share-link" lass="adf-share-link" matInput placeholder="{{ 'SHARE.PUBLIC-LINK' | translate }}"
|
||||
[value]="baseShareUrl + sharedId">
|
||||
<mat-icon matPrefix>link</mat-icon>
|
||||
</mat-form-field>
|
||||
</form>
|
||||
|
||||
@@ -17,93 +17,112 @@
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { ComponentFixture } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { ShareDialogComponent } from './share.dialog';
|
||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||
import { SharedLinksApiService, setupTestBed } from '@alfresco/adf-core';
|
||||
|
||||
describe('ShareDialogComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<ShareDialogComponent>;
|
||||
let spyCreate: any;
|
||||
let spyDelete: any;
|
||||
let component: ShareDialogComponent;
|
||||
let dialogRef;
|
||||
let sharedLinksApiService: SharedLinksApiService;
|
||||
const dialogRef = {
|
||||
close: jasmine.createSpy('close')
|
||||
};
|
||||
|
||||
let data: any = {
|
||||
node: { entry: { properties: { 'qshare:sharedId': 'example-link' }, name: 'example-name' } }
|
||||
node: { entry: { properties: { 'qshare:sharedId': 'example-link' }, name: 'example-name' } },
|
||||
baseShareUrl: 'baseShareUrl-example'
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
dialogRef = {
|
||||
close: jasmine.createSpy('close')
|
||||
};
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
BrowserDynamicTestingModule
|
||||
],
|
||||
declarations: [
|
||||
ShareDialogComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef },
|
||||
{
|
||||
provide: MAT_DIALOG_DATA,
|
||||
useValue: data
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
TestBed.overrideModule(BrowserDynamicTestingModule, {
|
||||
set: { entryComponents: [ShareDialogComponent] }
|
||||
});
|
||||
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: data }
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShareDialogComponent);
|
||||
sharedLinksApiService = TestBed.get(SharedLinksApiService);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
spyCreate = spyOn(sharedLinksApiService, 'createSharedLinks').and.returnValue(Observable.of({ entry: { id: 'test-sharedId' } }));
|
||||
spyDelete = spyOn(sharedLinksApiService, 'deleteSharedLink').and.returnValue(Observable.of({}));
|
||||
});
|
||||
|
||||
it('should init the dialog with the file name and baseShareUrl', () => {
|
||||
expect(component.fileName).toBe('example-name');
|
||||
expect(component.baseShareUrl).toBe('baseShareUrl-example');
|
||||
});
|
||||
it('should init the dialog with the file name and baseShareUrl', async(() => {
|
||||
component.data = {
|
||||
baseShareUrl: 'base-url/',
|
||||
node: { entry: { properties: { 'qshare:sharedId': 'example-link' }, name: 'example-name' } }
|
||||
};
|
||||
|
||||
component.ngOnInit();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(fixture.nativeElement.querySelector('#adf-share-link').value).toBe('base-url/example-link');
|
||||
});
|
||||
}));
|
||||
|
||||
describe('public link creation', () => {
|
||||
|
||||
it('should not create the public link if it alredy present in the node', () => {
|
||||
let spyCreate = spyOn(component, 'createSharedLinks').and.returnValue(Observable.of(''));
|
||||
it('should not create the public link if it already present in the node', () => {
|
||||
component.data = {
|
||||
node: { entry: { properties: { 'qshare:sharedId': 'example-link' }, name: 'example-name' } }
|
||||
};
|
||||
|
||||
component.ngOnInit();
|
||||
|
||||
expect(spyCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not create the public link if it alredy present in the node', () => {
|
||||
it('should not create the public link if is not present in the node', () => {
|
||||
component.data = {
|
||||
node: { entry: { name: 'example-name' } }
|
||||
baseShareUrl: 'baseShareUrl-example'
|
||||
};
|
||||
|
||||
let spyCreate = spyOn(component, 'createSharedLinks').and.returnValue(Observable.of(''));
|
||||
|
||||
data = {
|
||||
node: { entry: { name: 'example-name' } }
|
||||
node: { entry: { properties: {}, name: 'example-name' } },
|
||||
baseShareUrl: 'baseShareUrl-example'
|
||||
};
|
||||
|
||||
component.ngOnInit();
|
||||
|
||||
expect(spyCreate).toHaveBeenCalled();
|
||||
expect(spyCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be able to delete the shared link', async(() => {
|
||||
component.data = {
|
||||
node: { entry: { name: 'example-name', properties: { 'qshare:sharedId': 'example-link' } } },
|
||||
baseShareUrl: 'baseShareUrl-example'
|
||||
};
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
fixture.nativeElement.querySelector('#adf-share-toggle-input').click();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(spyDelete).toHaveBeenCalled();
|
||||
expect(component.data.node.entry.properties['qshare:sharedId']).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should show the toggle disabled when the shareid property is null', async(() => {
|
||||
component.data = {
|
||||
node: { entry: { name: 'example-name', properties: { 'qshare:sharedId': null } } },
|
||||
baseShareUrl: 'baseShareUrl-example'
|
||||
};
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(fixture.nativeElement.querySelector('#adf-share-toggle')).not.toBeNull();
|
||||
expect(fixture.nativeElement.querySelector('#adf-share-toggle.mat-checked')).toBeNull();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -52,7 +52,8 @@ export class ShareDialogComponent implements OnInit {
|
||||
this.sharedId = this.data.node.entry.properties['qshare:sharedId'];
|
||||
this.isFileShared = true;
|
||||
} else {
|
||||
this.createSharedLinks(this.data.node.entry.id);
|
||||
this.isFileShared = false;
|
||||
this.isDisabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,6 +75,7 @@ export class ShareDialogComponent implements OnInit {
|
||||
this.sharedLinksApiService.createSharedLinks(nodeId).subscribe((sharedLink: SharedLinkEntry) => {
|
||||
if (sharedLink.entry) {
|
||||
this.sharedId = sharedLink.entry.id;
|
||||
this.data.node.entry.properties['qshare:sharedId'] = this.sharedId;
|
||||
this.isFileShared = true;
|
||||
this.isDisabled = false;
|
||||
}
|
||||
@@ -86,6 +88,7 @@ export class ShareDialogComponent implements OnInit {
|
||||
|
||||
deleteSharedLink(sharedId: string) {
|
||||
this.sharedLinksApiService.deleteSharedLink(sharedId).subscribe(() => {
|
||||
this.data.node.entry.properties['qshare:sharedId'] = null;
|
||||
this.isFileShared = false;
|
||||
this.isDisabled = false;
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ import { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { Component, DebugElement } from '@angular/core';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiService, setupTestBed, CoreModule, AlfrescoApiServiceMock } from '@alfresco/adf-core';
|
||||
|
||||
import { DialogModule } from '../dialogs/dialog.module';
|
||||
import { NodeDownloadDirective } from './node-download.directive';
|
||||
@@ -40,17 +40,22 @@ describe('NodeDownloadDirective', () => {
|
||||
let contentService;
|
||||
let dialogSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DialogModule
|
||||
],
|
||||
declarations: [
|
||||
TestComponent,
|
||||
NodeDownloadDirective
|
||||
]
|
||||
});
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot(),
|
||||
DialogModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }
|
||||
// { provide: AppConfigService, useValue: AppConfigServiceMock },
|
||||
],
|
||||
declarations: [
|
||||
TestComponent,
|
||||
NodeDownloadDirective
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.debugElement.query(By.directive(NodeDownloadDirective));
|
||||
@@ -90,6 +95,17 @@ describe('NodeDownloadDirective', () => {
|
||||
expect(contentService.getContentUrl).toHaveBeenCalledWith(node.entry.id, true);
|
||||
});
|
||||
|
||||
it('should download selected shared node as file', () => {
|
||||
spyOn(contentService, 'getContentUrl');
|
||||
const node = { entry: { nodeId: 'shared-node-id', isFile: true } };
|
||||
component.selection = [node];
|
||||
|
||||
fixture.detectChanges();
|
||||
element.triggerEventHandler('click', null);
|
||||
|
||||
expect(contentService.getContentUrl).toHaveBeenCalledWith(node.entry.nodeId, true);
|
||||
});
|
||||
|
||||
it('should download selected files nodes as zip', () => {
|
||||
const node1 = { entry: { id: 'node-1' } };
|
||||
const node2 = { entry: { id: 'node-2' } };
|
||||
@@ -101,6 +117,17 @@ describe('NodeDownloadDirective', () => {
|
||||
expect(dialogSpy.calls.argsFor(0)[1].data).toEqual({ nodeIds: [ 'node-1', 'node-2' ] });
|
||||
});
|
||||
|
||||
it('should download selected shared files nodes as zip', () => {
|
||||
const node1 = { entry: { nodeId: 'shared-node-1' } };
|
||||
const node2 = { entry: { nodeId: 'shared-node-2' } };
|
||||
component.selection = [node1, node2];
|
||||
|
||||
fixture.detectChanges();
|
||||
element.triggerEventHandler('click', null);
|
||||
|
||||
expect(dialogSpy.calls.argsFor(0)[1].data).toEqual({ nodeIds: [ 'shared-node-1', 'shared-node-2' ] });
|
||||
});
|
||||
|
||||
it('should download selected folder node as zip', () => {
|
||||
const node = { entry: { isFolder: true, id: 'node-id' } };
|
||||
component.selection = [node];
|
||||
|
||||
@@ -86,8 +86,10 @@ export class NodeDownloadDirective {
|
||||
private downloadFile(node: MinimalNodeEntity) {
|
||||
if (node && node.entry) {
|
||||
const contentApi = this.apiService.getInstance().content;
|
||||
// nodeId for Shared node
|
||||
const id = (<any> node.entry).nodeId || node.entry.id;
|
||||
|
||||
const url = contentApi.getContentUrl(node.entry.id, true);
|
||||
const url = contentApi.getContentUrl(id, true);
|
||||
const fileName = node.entry.name;
|
||||
|
||||
this.download(url, fileName);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TestBed, ComponentFixture, async, fakeAsync } from '@angular/core/testing';
|
||||
import { TestBed, ComponentFixture, fakeAsync } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Component, DebugElement } from '@angular/core';
|
||||
|
||||
@@ -24,6 +24,8 @@ import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { NodeActionsService } from '../document-list/services/node-actions.service';
|
||||
import { ContentNodeDialogService } from '../content-node-selector/content-node-dialog.service';
|
||||
import { DocumentListService } from '../document-list/services/document-list.service';
|
||||
import { setupTestBed } from '../../core/testing';
|
||||
import { CoreModule } from '@alfresco/adf-core';
|
||||
|
||||
const fakeNode: MinimalNodeEntryEntity = <MinimalNodeEntryEntity> {
|
||||
id: 'fake',
|
||||
@@ -44,22 +46,20 @@ describe('NodeLock Directive', () => {
|
||||
let element: DebugElement;
|
||||
let contentNodeDialogService: ContentNodeDialogService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
NodeActionsService,
|
||||
ContentNodeDialogService,
|
||||
DocumentListService
|
||||
],
|
||||
declarations: [
|
||||
TestComponent,
|
||||
NodeLockDirective
|
||||
]
|
||||
});
|
||||
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
],
|
||||
providers: [
|
||||
NodeActionsService,
|
||||
ContentNodeDialogService,
|
||||
DocumentListService
|
||||
],
|
||||
declarations: [
|
||||
TestComponent,
|
||||
NodeLockDirective
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
|
||||
@@ -27,6 +27,7 @@ import { ContentNodeDialogService } from '../content-node-selector/content-node-
|
||||
})
|
||||
export class NodeLockDirective implements AfterViewInit {
|
||||
|
||||
/** Node to lock/unlock. */
|
||||
@Input('adf-node-lock')
|
||||
node: MinimalNodeEntryEntity;
|
||||
|
||||
|
||||
+7
-21
@@ -16,36 +16,22 @@
|
||||
*/
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { DataTableModule } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from '../../services/document-list.service';
|
||||
import { CustomResourcesService } from '../../services/custom-resources.service';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { ContentActionModel } from './../../models/content-action.model';
|
||||
import { DocumentListComponent } from './../document-list.component';
|
||||
import { ContentActionListComponent } from './content-action-list.component';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('ContentColumnList', () => {
|
||||
|
||||
let documentList: DocumentListComponent;
|
||||
let actionList: ContentActionListComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent
|
||||
],
|
||||
providers: [
|
||||
DocumentListService,
|
||||
CustomResourcesService
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
documentList = (TestBed.createComponent(DocumentListComponent).componentInstance as DocumentListComponent);
|
||||
|
||||
+11
@@ -42,4 +42,15 @@ export class ContentActionListComponent {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unregisterAction(action: ContentActionModel): boolean {
|
||||
if (this.documentList && action) {
|
||||
const idx = this.documentList.actions.indexOf(action);
|
||||
if (idx >= 0) {
|
||||
this.documentList.actions.splice(idx, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+25
-22
@@ -15,14 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, SimpleChange } from '@angular/core';
|
||||
import { EventEmitter } from '@angular/core';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { ContentService } from '@alfresco/adf-core';
|
||||
import { DataTableModule } from '@alfresco/adf-core';
|
||||
import { ContentService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { FileNode } from '../../../mock';
|
||||
import { DocumentListService } from '../../services/document-list.service';
|
||||
import { CustomResourcesService } from '../../services/custom-resources.service';
|
||||
import { ContentActionHandler } from './../../models/content-action.model';
|
||||
import { DocumentActionsService } from './../../services/document-actions.service';
|
||||
import { FolderActionsService } from './../../services/folder-actions.service';
|
||||
@@ -31,6 +28,7 @@ import { DocumentListComponent } from './../document-list.component';
|
||||
import { ContentActionListComponent } from './content-action-list.component';
|
||||
import { ContentActionComponent } from './content-action.component';
|
||||
import { ContentActionModel } from './../../models/content-action.model';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('ContentAction', () => {
|
||||
|
||||
@@ -42,23 +40,10 @@ describe('ContentAction', () => {
|
||||
let contentService: ContentService;
|
||||
let nodeActionsService: NodeActionsService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule
|
||||
],
|
||||
providers: [
|
||||
DocumentListService,
|
||||
CustomResourcesService
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
contentService = TestBed.get(ContentService);
|
||||
@@ -96,6 +81,24 @@ describe('ContentAction', () => {
|
||||
expect(model.icon).toBe(action.icon);
|
||||
});
|
||||
|
||||
it('should update visibility binding', () => {
|
||||
let action = new ContentActionComponent(actionList, null, null);
|
||||
action.target = 'document';
|
||||
action.title = '<title>';
|
||||
action.icon = '<icon>';
|
||||
|
||||
action.visible = true;
|
||||
action.ngOnInit();
|
||||
expect(action.documentActionModel.visible).toBeTruthy();
|
||||
|
||||
action.visible = false;
|
||||
action.ngOnChanges({
|
||||
'visible': new SimpleChange(true, false, false)
|
||||
});
|
||||
|
||||
expect(action.documentActionModel.visible).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should get action handler from document actions service', () => {
|
||||
|
||||
let handler = function () {
|
||||
|
||||
+89
-35
@@ -17,13 +17,14 @@
|
||||
|
||||
/* tslint:disable:component-selector */
|
||||
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges, OnDestroy } from '@angular/core';
|
||||
|
||||
import { ContentActionHandler } from '../../models/content-action.model';
|
||||
import { DocumentActionsService } from '../../services/document-actions.service';
|
||||
import { FolderActionsService } from '../../services/folder-actions.service';
|
||||
import { ContentActionModel, ContentActionTarget } from './../../models/content-action.model';
|
||||
import { ContentActionListComponent } from './content-action-list.component';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
|
||||
@Component({
|
||||
selector: 'content-action',
|
||||
@@ -33,7 +34,7 @@ import { ContentActionListComponent } from './content-action-list.component';
|
||||
FolderActionsService
|
||||
]
|
||||
})
|
||||
export class ContentActionComponent implements OnInit {
|
||||
export class ContentActionComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
/** The title of the action as shown in the menu. */
|
||||
@Input()
|
||||
@@ -43,11 +44,15 @@ export class ContentActionComponent implements OnInit {
|
||||
@Input()
|
||||
icon: string;
|
||||
|
||||
/** Visibility state (see examples). */
|
||||
@Input()
|
||||
visible: boolean | Function = true;
|
||||
|
||||
/** System actions. Can be "delete", "download", "copy" or "move". */
|
||||
@Input()
|
||||
handler: string;
|
||||
|
||||
/** Type of item that the action appies to. Can be "document" or "folder" */
|
||||
/** Type of item that the action applies to. Can be "document" or "folder" */
|
||||
@Input()
|
||||
target: string = ContentActionTarget.All;
|
||||
|
||||
@@ -61,7 +66,7 @@ export class ContentActionComponent implements OnInit {
|
||||
|
||||
/** Is the menu item disabled? */
|
||||
@Input()
|
||||
disabled: boolean = false;
|
||||
disabled: boolean | Function = false;
|
||||
|
||||
/** Emitted when the user selects the action from the menu. */
|
||||
@Output()
|
||||
@@ -83,6 +88,11 @@ export class ContentActionComponent implements OnInit {
|
||||
@Output()
|
||||
success = new EventEmitter();
|
||||
|
||||
documentActionModel: ContentActionModel;
|
||||
folderActionModel: ContentActionModel;
|
||||
|
||||
private subscriptions: Subscription[] = [];
|
||||
|
||||
constructor(
|
||||
private list: ContentActionListComponent,
|
||||
private documentActions: DocumentActionsService,
|
||||
@@ -91,10 +101,45 @@ export class ContentActionComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
if (this.target === ContentActionTarget.All) {
|
||||
this.generateAction(ContentActionTarget.Folder);
|
||||
this.generateAction(ContentActionTarget.Document);
|
||||
this.folderActionModel = this.generateAction(ContentActionTarget.Folder);
|
||||
this.documentActionModel = this.generateAction(ContentActionTarget.Document);
|
||||
} else {
|
||||
this.generateAction(this.target);
|
||||
this.documentActionModel = this.generateAction(this.target);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes.visible && !changes.visible.firstChange) {
|
||||
if (this.documentActionModel) {
|
||||
this.documentActionModel.visible = changes.visible.currentValue;
|
||||
}
|
||||
if (this.folderActionModel) {
|
||||
this.folderActionModel.visible = changes.visible.currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.disabled && !changes.disabled.firstChange) {
|
||||
if (this.documentActionModel) {
|
||||
this.documentActionModel.disabled = changes.disabled.currentValue;
|
||||
}
|
||||
if (this.folderActionModel) {
|
||||
this.folderActionModel.disabled = changes.disabled.currentValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
||||
this.subscriptions = [];
|
||||
|
||||
if (this.documentActionModel) {
|
||||
this.unregister(this.documentActionModel);
|
||||
this.documentActionModel = null;
|
||||
}
|
||||
|
||||
if (this.folderActionModel) {
|
||||
this.unregister(this.folderActionModel);
|
||||
this.folderActionModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,14 +150,22 @@ export class ContentActionComponent implements OnInit {
|
||||
return false;
|
||||
}
|
||||
|
||||
private generateAction(target: string) {
|
||||
let model = new ContentActionModel({
|
||||
unregister(model: ContentActionModel): boolean {
|
||||
if (this.list) {
|
||||
return this.list.unregisterAction(model);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private generateAction(target: string): ContentActionModel {
|
||||
const model = new ContentActionModel({
|
||||
title: this.title,
|
||||
icon: this.icon,
|
||||
permission: this.permission,
|
||||
disableWithNoPermission: this.disableWithNoPermission,
|
||||
target: target,
|
||||
disabled: this.disabled
|
||||
disabled: this.disabled,
|
||||
visible: this.visible
|
||||
});
|
||||
if (this.handler) {
|
||||
model.handler = this.getSystemHandler(target, this.handler);
|
||||
@@ -125,44 +178,45 @@ export class ContentActionComponent implements OnInit {
|
||||
}
|
||||
|
||||
this.register(model);
|
||||
return model;
|
||||
}
|
||||
|
||||
getSystemHandler(target: string, name: string): ContentActionHandler {
|
||||
if (target) {
|
||||
let ltarget = target.toLowerCase();
|
||||
target = target.toLowerCase();
|
||||
|
||||
if (ltarget === ContentActionTarget.Document) {
|
||||
if (target === ContentActionTarget.Document) {
|
||||
if (this.documentActions) {
|
||||
this.documentActions.permissionEvent.subscribe((permission) => {
|
||||
this.permissionEvent.emit(permission);
|
||||
});
|
||||
|
||||
this.documentActions.error.subscribe((errors) => {
|
||||
this.error.emit(errors);
|
||||
});
|
||||
|
||||
this.documentActions.success.subscribe((message) => {
|
||||
this.success.emit(message);
|
||||
});
|
||||
this.subscriptions.push(
|
||||
this.documentActions.permissionEvent.subscribe(permission => {
|
||||
this.permissionEvent.emit(permission);
|
||||
}),
|
||||
this.documentActions.error.subscribe(errors => {
|
||||
this.error.emit(errors);
|
||||
}),
|
||||
this.documentActions.success.subscribe(message => {
|
||||
this.success.emit(message);
|
||||
})
|
||||
);
|
||||
|
||||
return this.documentActions.getHandler(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ltarget === ContentActionTarget.Folder) {
|
||||
if (target === ContentActionTarget.Folder) {
|
||||
if (this.folderActions) {
|
||||
this.folderActions.permissionEvent.subscribe((permission) => {
|
||||
this.permissionEvent.emit(permission);
|
||||
});
|
||||
|
||||
this.folderActions.error.subscribe((errors) => {
|
||||
this.error.emit(errors);
|
||||
});
|
||||
|
||||
this.folderActions.success.subscribe((message) => {
|
||||
this.success.emit(message);
|
||||
});
|
||||
this.subscriptions.push(
|
||||
this.folderActions.permissionEvent.subscribe(permission => {
|
||||
this.permissionEvent.emit(permission);
|
||||
}),
|
||||
this.folderActions.error.subscribe(errors => {
|
||||
this.error.emit(errors);
|
||||
}),
|
||||
this.folderActions.success.subscribe(message => {
|
||||
this.success.emit(message);
|
||||
})
|
||||
);
|
||||
|
||||
return this.folderActions.getHandler(name);
|
||||
}
|
||||
|
||||
+7
-22
@@ -16,14 +16,12 @@
|
||||
*/
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { DataColumn, DataTableModule } from '@alfresco/adf-core';
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { DataColumn, setupTestBed } from '@alfresco/adf-core';
|
||||
import { LogService } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from '../../services/document-list.service';
|
||||
import { CustomResourcesService } from '../../services/custom-resources.service';
|
||||
import { DocumentListComponent } from './../document-list.component';
|
||||
import { ContentColumnListComponent } from './content-column-list.component';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('ContentColumnList', () => {
|
||||
|
||||
@@ -31,23 +29,10 @@ describe('ContentColumnList', () => {
|
||||
let columnList: ContentColumnListComponent;
|
||||
let logService: LogService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent
|
||||
],
|
||||
providers: [
|
||||
CustomResourcesService,
|
||||
DocumentListService
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
documentList = (TestBed.createComponent(DocumentListComponent).componentInstance as DocumentListComponent);
|
||||
|
||||
+7
-23
@@ -16,14 +16,12 @@
|
||||
*/
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { LogService } from '@alfresco/adf-core';
|
||||
import { DataTableModule } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from '../../services/document-list.service';
|
||||
import { CustomResourcesService } from '../../services/custom-resources.service';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { LogService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { DocumentListComponent } from './../document-list.component';
|
||||
import { ContentColumnListComponent } from './content-column-list.component';
|
||||
import { ContentColumnComponent } from './content-column.component';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('ContentColumn', () => {
|
||||
|
||||
@@ -31,24 +29,10 @@ describe('ContentColumn', () => {
|
||||
let columnList: ContentColumnListComponent;
|
||||
let logService: LogService;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent
|
||||
],
|
||||
providers: [
|
||||
CustomResourcesService,
|
||||
DocumentListService,
|
||||
LogService
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
documentList = (TestBed.createComponent(DocumentListComponent).componentInstance as DocumentListComponent);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
[display]="display"
|
||||
[noPermission]="noPermission"
|
||||
[showHeader]="!isEmpty() && showHeader"
|
||||
[rowMenuCacheEnabled]="false"
|
||||
(showRowContextMenu)="onShowRowContextMenu($event)"
|
||||
(showRowActionsMenu)="onShowRowActionsMenu($event)"
|
||||
(executeRowAction)="onExecuteRowAction($event)"
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgZone, SimpleChange, TemplateRef } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { AlfrescoApiService, TranslationService } from '@alfresco/adf-core';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, SimpleChange, TemplateRef, QueryList } from '@angular/core';
|
||||
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { AlfrescoApiService, DataColumnListComponent, DataColumnComponent } from '@alfresco/adf-core';
|
||||
import { DataColumn, DataTableComponent } from '@alfresco/adf-core';
|
||||
import { DataTableModule } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { FileNode, FolderNode } from '../../mock';
|
||||
@@ -38,8 +37,8 @@ import { RowFilter } from './../data/row-filter.model';
|
||||
import { DocumentListService } from './../services/document-list.service';
|
||||
import { CustomResourcesService } from './../services/custom-resources.service';
|
||||
import { DocumentListComponent } from './document-list.component';
|
||||
|
||||
declare let jasmine: any;
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { ContentTestingModule } from '../../testing/content.testing.module';
|
||||
|
||||
describe('DocumentList', () => {
|
||||
|
||||
@@ -50,25 +49,13 @@ describe('DocumentList', () => {
|
||||
let fixture: ComponentFixture<DocumentListComponent>;
|
||||
let element: HTMLElement;
|
||||
let eventMock: any;
|
||||
let spyGetSites: any;
|
||||
let spyFavorite: any;
|
||||
|
||||
beforeEach(async(() => {
|
||||
let zone = new NgZone({ enableLongStackTrace: false });
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent
|
||||
],
|
||||
providers: [
|
||||
DocumentListService,
|
||||
CustomResourcesService,
|
||||
{ provide: NgZone, useValue: zone }
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
eventMock = {
|
||||
@@ -78,35 +65,47 @@ describe('DocumentList', () => {
|
||||
|
||||
fixture = TestBed.createComponent(DocumentListComponent);
|
||||
|
||||
let translateService = TestBed.get(TranslationService);
|
||||
spyOn(translateService, 'get').and.callFake((key) => {
|
||||
return Observable.of(key);
|
||||
});
|
||||
|
||||
element = fixture.nativeElement;
|
||||
documentList = fixture.componentInstance;
|
||||
documentListService = TestBed.get(DocumentListService);
|
||||
apiService = TestBed.get(AlfrescoApiService);
|
||||
customResourcesService = TestBed.get(CustomResourcesService);
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
documentList.ngOnInit();
|
||||
|
||||
beforeEach(() => {
|
||||
jasmine.Ajax.install();
|
||||
spyGetSites = spyOn(apiService.sitesApi, 'getSites').and.returnValue(Promise.resolve(fakeGetSitesAnswer));
|
||||
spyFavorite = spyOn(apiService.favoritesApi, 'getFavorites').and.returnValue(Promise.resolve({list: []}));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.Ajax.uninstall();
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should update schema if columns change', fakeAsync(() => {
|
||||
documentList.columnList = new DataColumnListComponent();
|
||||
documentList.columnList.columns = new QueryList<DataColumnComponent>();
|
||||
|
||||
spyOn(documentList.data, 'setColumns').and.callThrough();
|
||||
|
||||
documentList.ngAfterContentInit();
|
||||
documentList.columnList.columns.reset([new DataColumnComponent()]);
|
||||
documentList.columnList.columns.notifyOnChanges();
|
||||
|
||||
tick(100);
|
||||
|
||||
expect(documentList.data.setColumns).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should setup default columns', () => {
|
||||
fixture.detectChanges();
|
||||
documentList.ngAfterContentInit();
|
||||
|
||||
expect(documentList.data.getColumns().length).not.toBe(0);
|
||||
});
|
||||
|
||||
it('should add the custom columns', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
let column = <DataColumn> {
|
||||
title: 'title',
|
||||
key: 'source',
|
||||
@@ -178,41 +177,29 @@ describe('DocumentList', () => {
|
||||
expect(action.execute).toHaveBeenCalledWith(node);
|
||||
});
|
||||
|
||||
it('should show the loading state during the loading of new elements', (done) => {
|
||||
it('should show the loading state during the loading of new elements', () => {
|
||||
documentList.ngOnInit();
|
||||
documentList.ngAfterContentInit();
|
||||
documentList.node = new NodePaging();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-document-list-loading')).toBeDefined();
|
||||
done();
|
||||
});
|
||||
expect(element.querySelector('.adf-document-list-loading')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should hide the header if showHeader is false', (done) => {
|
||||
it('should hide the header if showHeader is false', () => {
|
||||
documentList.showHeader = false;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-datatable-header')).toBe(null);
|
||||
done();
|
||||
});
|
||||
expect(element.querySelector('.adf-datatable-header')).toBe(null);
|
||||
});
|
||||
|
||||
it('should show the header if showHeader is true', (done) => {
|
||||
it('should show the header if showHeader is true', () => {
|
||||
documentList.showHeader = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-datatable-header')).toBeDefined();
|
||||
done();
|
||||
});
|
||||
expect(element.querySelector('.adf-datatable-header')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should reset selection upon reload', () => {
|
||||
@@ -224,29 +211,21 @@ describe('DocumentList', () => {
|
||||
expect(documentList.resetSelection).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use the cardview style if cardview is true', (done) => {
|
||||
it('should use the cardview style if cardview is true', () => {
|
||||
documentList.display = 'gallery';
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-data-table-card')).toBeDefined();
|
||||
done();
|
||||
});
|
||||
expect(element.querySelector('.adf-data-table-card')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should use the base document list style if cardview is false', (done) => {
|
||||
it('should use the base document list style if cardview is false', () => {
|
||||
documentList.display = 'list';
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('.adf-data-table-card')).toBe(null);
|
||||
expect(element.querySelector('.adf-data-table')).toBeDefined();
|
||||
done();
|
||||
});
|
||||
expect(element.querySelector('.adf-data-table-card')).toBe(null);
|
||||
expect(element.querySelector('.adf-data-table')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should reset selection upon reload', () => {
|
||||
@@ -258,7 +237,7 @@ describe('DocumentList', () => {
|
||||
expect(documentList.resetSelection).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reset when a prameter changes', () => {
|
||||
it('should reset when a parameter changes', () => {
|
||||
spyOn(documentList.dataTable, 'resetSelection').and.callThrough();
|
||||
|
||||
documentList.ngOnChanges({});
|
||||
@@ -266,25 +245,19 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should empty template be present when no element are present', (done) => {
|
||||
documentList.currentFolderId = '1d26e465-dea3-42f3-b415-faa8364b9692';
|
||||
fixture.detectChanges();
|
||||
documentList.folderNode = new NodeMinimal();
|
||||
documentList.folderNode.id = '1d26e465-dea3-42f3-b415-faa8364b9692';
|
||||
|
||||
documentList.reload();
|
||||
spyOn(documentListService, 'getFolder').and.returnValue(Observable.of(fakeNodeAnswerWithNOEntries));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
documentList.ready.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
let disposableReady = documentList.ready.subscribe(() => {
|
||||
expect(element.querySelector('#adf-document-list-empty')).toBeDefined();
|
||||
disposableReady.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
jasmine.Ajax.requests.at(0).respondWith({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
responseText: JSON.stringify(fakeNodeAnswerWithNOEntries)
|
||||
});
|
||||
documentList.reload();
|
||||
});
|
||||
|
||||
it('should not execute action without node provided', () => {
|
||||
@@ -344,6 +317,70 @@ describe('DocumentList', () => {
|
||||
|
||||
});
|
||||
|
||||
it('should not display hidden content actions', () => {
|
||||
documentList.actions = [
|
||||
new ContentActionModel({
|
||||
target: 'document',
|
||||
title: 'Action1',
|
||||
visible: false
|
||||
}),
|
||||
new ContentActionModel({
|
||||
target: 'document',
|
||||
title: 'Action2',
|
||||
visible: true
|
||||
})
|
||||
];
|
||||
|
||||
const nodeFile = { entry: { isFile: true, name: 'xyz' } };
|
||||
const actions = documentList.getNodeActions(nodeFile);
|
||||
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toBe('Action2');
|
||||
});
|
||||
|
||||
it('should evaluate conditional visibility for content actions', () => {
|
||||
documentList.actions = [
|
||||
new ContentActionModel({
|
||||
target: 'document',
|
||||
title: 'Action1',
|
||||
visible: (): boolean => true
|
||||
}),
|
||||
new ContentActionModel({
|
||||
target: 'document',
|
||||
title: 'Action2',
|
||||
visible: (): boolean => false
|
||||
})
|
||||
];
|
||||
|
||||
const nodeFile = { entry: { isFile: true, name: 'xyz' } };
|
||||
const actions = documentList.getNodeActions(nodeFile);
|
||||
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toBe('Action1');
|
||||
});
|
||||
|
||||
it('should evaluate conditional disabled state for content action', () => {
|
||||
documentList.actions = [
|
||||
new ContentActionModel({
|
||||
target: 'document',
|
||||
title: 'Action1',
|
||||
disabled: (): boolean => true
|
||||
}),
|
||||
new ContentActionModel({
|
||||
target: 'document',
|
||||
title: 'Action2',
|
||||
disabled: (): boolean => false
|
||||
})
|
||||
];
|
||||
|
||||
const nodeFile = { entry: { isFile: true, name: 'xyz' } };
|
||||
const actions = documentList.getNodeActions(nodeFile);
|
||||
|
||||
expect(actions.length).toBe(2);
|
||||
expect(actions[0].disabled).toBeTruthy();
|
||||
expect(actions[1].disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not disable the action if there is copy permission', () => {
|
||||
let documentMenu = new ContentActionModel({
|
||||
disableWithNoPermission: true,
|
||||
@@ -386,46 +423,6 @@ describe('DocumentList', () => {
|
||||
|
||||
});
|
||||
|
||||
it('should disable the action if there is no permission for the file and disableWithNoPermission false', () => {
|
||||
let documentMenu = new ContentActionModel({
|
||||
disableWithNoPermission: false,
|
||||
permission: 'delete',
|
||||
target: 'document',
|
||||
title: 'FileAction'
|
||||
});
|
||||
|
||||
documentList.actions = [
|
||||
documentMenu
|
||||
];
|
||||
|
||||
let nodeFile = { entry: { isFile: true, name: 'xyz', allowableOperations: ['create', 'update'] } };
|
||||
|
||||
let actions = documentList.getNodeActions(nodeFile);
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toEqual('FileAction');
|
||||
expect(actions[0].disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should disable the action if there is no permission for the folder and disableWithNoPermission false', () => {
|
||||
let documentMenu = new ContentActionModel({
|
||||
disableWithNoPermission: false,
|
||||
permission: 'delete',
|
||||
target: 'folder',
|
||||
title: 'FolderAction'
|
||||
});
|
||||
|
||||
documentList.actions = [
|
||||
documentMenu
|
||||
];
|
||||
|
||||
let nodeFile = { entry: { isFolder: true, name: 'xyz', allowableOperations: ['create', 'update'] } };
|
||||
|
||||
let actions = documentList.getNodeActions(nodeFile);
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toEqual('FolderAction');
|
||||
expect(actions[0].disabled).toBe(true);
|
||||
});
|
||||
|
||||
it('should not disable the action if there is the right permission for the file', () => {
|
||||
let documentMenu = new ContentActionModel({
|
||||
disableWithNoPermission: true,
|
||||
@@ -443,7 +440,7 @@ describe('DocumentList', () => {
|
||||
let actions = documentList.getNodeActions(nodeFile);
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toEqual('FileAction');
|
||||
expect(actions[0].disabled).toBeUndefined();
|
||||
expect(actions[0].disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not disable the action if there is the right permission for the folder', () => {
|
||||
@@ -463,14 +460,15 @@ describe('DocumentList', () => {
|
||||
let actions = documentList.getNodeActions(nodeFile);
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toEqual('FolderAction');
|
||||
expect(actions[0].disabled).toBeUndefined();
|
||||
expect(actions[0].disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not disable the action if there are no permissions for the file', () => {
|
||||
it('should not disable the action if there are no permissions for the file and disable with no permission is false', () => {
|
||||
let documentMenu = new ContentActionModel({
|
||||
permission: 'delete',
|
||||
target: 'document',
|
||||
title: 'FileAction'
|
||||
title: 'FileAction',
|
||||
disableWithNoPermission: false
|
||||
});
|
||||
|
||||
documentList.actions = [
|
||||
@@ -482,14 +480,15 @@ describe('DocumentList', () => {
|
||||
let actions = documentList.getNodeActions(nodeFile);
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toEqual('FileAction');
|
||||
expect(actions[0].disabled).toBeUndefined();
|
||||
expect(actions[0].disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not disable the action if there are no permissions for the folder', () => {
|
||||
it('should not disable the action if there are no permissions for the folder and disable with no permission is false', () => {
|
||||
let documentMenu = new ContentActionModel({
|
||||
permission: 'delete',
|
||||
target: 'folder',
|
||||
title: 'FolderAction'
|
||||
title: 'FolderAction',
|
||||
disableWithNoPermission: false
|
||||
});
|
||||
|
||||
documentList.actions = [
|
||||
@@ -501,7 +500,49 @@ describe('DocumentList', () => {
|
||||
let actions = documentList.getNodeActions(nodeFile);
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toEqual('FolderAction');
|
||||
expect(actions[0].disabled).toBeUndefined();
|
||||
expect(actions[0].disabled).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should disable the action if there are no permissions for the file and disable with no permission is true', () => {
|
||||
let documentMenu = new ContentActionModel({
|
||||
permission: 'delete',
|
||||
target: 'document',
|
||||
title: 'FileAction',
|
||||
disableWithNoPermission: true
|
||||
});
|
||||
|
||||
documentList.actions = [
|
||||
documentMenu
|
||||
];
|
||||
|
||||
let nodeFile = { entry: { isFile: true, name: 'xyz', allowableOperations: null } };
|
||||
|
||||
let actions = documentList.getNodeActions(nodeFile);
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toEqual('FileAction');
|
||||
expect(actions[0].disabled).toBeDefined();
|
||||
expect(actions[0].disabled).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should disable the action if there are no permissions for the folder and disable with no permission is true', () => {
|
||||
let documentMenu = new ContentActionModel({
|
||||
permission: 'delete',
|
||||
target: 'folder',
|
||||
title: 'FolderAction',
|
||||
disableWithNoPermission: true
|
||||
});
|
||||
|
||||
documentList.actions = [
|
||||
documentMenu
|
||||
];
|
||||
|
||||
let nodeFile = { entry: { isFolder: true, name: 'xyz', allowableOperations: null } };
|
||||
|
||||
let actions = documentList.getNodeActions(nodeFile);
|
||||
expect(actions.length).toBe(1);
|
||||
expect(actions[0].title).toEqual('FolderAction');
|
||||
expect(actions[0].disabled).toBeDefined();
|
||||
expect(actions[0].disabled).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should find no content actions', () => {
|
||||
@@ -520,8 +561,9 @@ describe('DocumentList', () => {
|
||||
|
||||
it('should emit nodeClick event', (done) => {
|
||||
let node = new FileNode();
|
||||
documentList.nodeClick.subscribe(e => {
|
||||
let disposableClick = documentList.nodeClick.subscribe(e => {
|
||||
expect(e.value).toBe(node);
|
||||
disposableClick.unsubscribe();
|
||||
done();
|
||||
});
|
||||
documentList.onNodeClick(node);
|
||||
@@ -609,8 +651,9 @@ describe('DocumentList', () => {
|
||||
|
||||
it('should emit file preview event on single click', (done) => {
|
||||
let file = new FileNode();
|
||||
documentList.preview.subscribe(e => {
|
||||
let disposablePreview = documentList.preview.subscribe(e => {
|
||||
expect(e.value).toBe(file);
|
||||
disposablePreview.unsubscribe();
|
||||
done();
|
||||
});
|
||||
documentList.navigationMode = DocumentListComponent.SINGLE_CLICK_NAVIGATION;
|
||||
@@ -619,8 +662,9 @@ describe('DocumentList', () => {
|
||||
|
||||
it('should emit file preview event on double click', (done) => {
|
||||
let file = new FileNode();
|
||||
documentList.preview.subscribe(e => {
|
||||
let disposablePreview = documentList.preview.subscribe(e => {
|
||||
expect(e.value).toBe(file);
|
||||
disposablePreview.unsubscribe();
|
||||
done();
|
||||
});
|
||||
documentList.navigationMode = DocumentListComponent.DOUBLE_CLICK_NAVIGATION;
|
||||
@@ -670,6 +714,8 @@ describe('DocumentList', () => {
|
||||
let folder = new FolderNode();
|
||||
let file = new FileNode();
|
||||
|
||||
spyOn(documentList, 'loadFolder').and.stub();
|
||||
|
||||
expect(documentList.performNavigation(folder)).toBeTruthy();
|
||||
expect(documentList.performNavigation(file)).toBeFalsy();
|
||||
expect(documentList.performNavigation(null)).toBeFalsy();
|
||||
@@ -708,6 +754,7 @@ describe('DocumentList', () => {
|
||||
it('should display folder content from loadFolder on reload if folderNode defined', () => {
|
||||
documentList.folderNode = new NodeMinimal();
|
||||
|
||||
spyOn(documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Observable.of(''));
|
||||
spyOn(documentList, 'loadFolder').and.callThrough();
|
||||
documentList.reload();
|
||||
expect(documentList.loadFolder).toHaveBeenCalled();
|
||||
@@ -715,7 +762,7 @@ describe('DocumentList', () => {
|
||||
|
||||
it('should display folder content from loadFolderByNodeId on reload if currentFolderId defined', () => {
|
||||
documentList.currentFolderId = 'id-folder';
|
||||
spyOn(documentList, 'loadFolderByNodeId').and.callThrough();
|
||||
spyOn(documentList, 'loadFolderByNodeId').and.stub();
|
||||
documentList.reload();
|
||||
expect(documentList.loadFolderByNodeId).toHaveBeenCalled();
|
||||
});
|
||||
@@ -829,6 +876,7 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should set row filter and reload contents if currentFolderId is set when setting rowFilter', () => {
|
||||
fixture.detectChanges();
|
||||
let filter = <RowFilter> {};
|
||||
documentList.currentFolderId = 'id';
|
||||
spyOn(documentList.data, 'setFilter').and.callThrough();
|
||||
@@ -850,6 +898,7 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should set image resolver for underlying adapter', () => {
|
||||
fixture.detectChanges();
|
||||
let resolver = <ImageResolver> {};
|
||||
spyOn(documentList.data, 'setImageResolver').and.callThrough();
|
||||
|
||||
@@ -869,8 +918,7 @@ describe('DocumentList', () => {
|
||||
it('should emit node-click DOM event', (done) => {
|
||||
let node = new NodeMinimalEntry();
|
||||
|
||||
const htmlElement = fixture.debugElement.nativeElement as HTMLElement;
|
||||
htmlElement.addEventListener('node-click', (e: CustomEvent) => {
|
||||
document.addEventListener('node-click', (e: CustomEvent) => {
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -888,8 +936,7 @@ describe('DocumentList', () => {
|
||||
it('should emit node-dblclick DOM event', (done) => {
|
||||
let node = new NodeMinimalEntry();
|
||||
|
||||
const htmlElement = fixture.debugElement.nativeElement as HTMLElement;
|
||||
htmlElement.addEventListener('node-dblclick', (e: CustomEvent) => {
|
||||
document.addEventListener('node-dblclick', (e: CustomEvent) => {
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -908,8 +955,9 @@ describe('DocumentList', () => {
|
||||
const error = { message: '{ "error": { "statusCode": 501 } }' };
|
||||
spyOn(documentListService, 'getFolderNode').and.returnValue(Observable.throw(error));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe(error);
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -921,21 +969,23 @@ describe('DocumentList', () => {
|
||||
spyOn(documentListService, 'getFolderNode').and.returnValue(Observable.of(fakeNodeWithCreatePermission));
|
||||
spyOn(documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.reject(error));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe(error);
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
documentList.loadFolderByNodeId('123');
|
||||
});
|
||||
|
||||
it('should set no permision when getFolderNode fails with 403', (done) => {
|
||||
it('should set no permission when getFolderNode fails with 403', (done) => {
|
||||
const error = { message: '{ "error": { "statusCode": 403 } }' };
|
||||
spyOn(documentListService, 'getFolderNode').and.returnValue(Observable.throw(error));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe(error);
|
||||
expect(documentList.noPermission).toBe(true);
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -953,6 +1003,7 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should reload contents if node data changes after previously got noPermission error', () => {
|
||||
fixture.detectChanges();
|
||||
spyOn(documentList.data, 'loadPage').and.callThrough();
|
||||
|
||||
documentList.noPermission = true;
|
||||
@@ -964,7 +1015,7 @@ describe('DocumentList', () => {
|
||||
expect(documentList.noPermission).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should noPermission be true if navigate to a folder with no permission', (done) => {
|
||||
it('should noPermission be true if navigate to a folder with no permission', () => {
|
||||
const error = { message: '{ "error": { "statusCode": 403 } }' };
|
||||
|
||||
documentList.currentFolderId = '1d26e465-dea3-42f3-b415-faa8364b9692';
|
||||
@@ -977,12 +1028,8 @@ describe('DocumentList', () => {
|
||||
documentList.loadFolder();
|
||||
let clickedFolderNode = new FolderNode('fake-folder-node');
|
||||
documentList.onNodeDblClick(clickedFolderNode);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(documentList.noPermission).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
expect(documentList.noPermission).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not perform navigation for virtual sources', () => {
|
||||
@@ -1008,8 +1055,9 @@ describe('DocumentList', () => {
|
||||
it('should emit error when fetch trashcan fails', (done) => {
|
||||
spyOn(apiService.nodesApi, 'getDeletedNodes').and.returnValue(Promise.reject('error'));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe('error');
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -1028,8 +1076,9 @@ describe('DocumentList', () => {
|
||||
spyOn(apiService.getInstance().core.sharedlinksApi, 'findSharedLinks')
|
||||
.and.returnValue(Promise.reject('error'));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe('error');
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -1038,18 +1087,17 @@ describe('DocumentList', () => {
|
||||
|
||||
it('should fetch sites', () => {
|
||||
const sitesApi = apiService.getInstance().core.sitesApi;
|
||||
spyOn(sitesApi, 'getSites').and.returnValue(Promise.resolve(null));
|
||||
|
||||
documentList.loadFolderByNodeId('-sites-');
|
||||
expect(sitesApi.getSites).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit error when fetch sites fails', (done) => {
|
||||
spyOn(apiService.getInstance().core.sitesApi, 'getSites')
|
||||
.and.returnValue(Promise.reject('error'));
|
||||
spyGetSites.and.returnValue(Promise.reject('error'));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe('error');
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -1057,36 +1105,34 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should assure that sites have name property set', (done) => {
|
||||
const sitesApi = apiService.getInstance().core.sitesApi;
|
||||
spyOn(sitesApi, 'getSites').and.returnValue(Promise.resolve(fakeGetSitesAnswer));
|
||||
fixture.detectChanges();
|
||||
|
||||
documentList.loadFolderByNodeId('-sites-');
|
||||
expect(sitesApi.getSites).toHaveBeenCalled();
|
||||
|
||||
documentList.ready.subscribe((page) => {
|
||||
let disposableReady = documentList.ready.subscribe((page) => {
|
||||
const entriesWithoutName = page.list.entries.filter(item => !item.entry.name);
|
||||
expect(entriesWithoutName.length).toBe(0);
|
||||
disposableReady.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
documentList.loadFolderByNodeId('-sites-');
|
||||
});
|
||||
|
||||
it('should assure that sites have name property set correctly', (done) => {
|
||||
const sitesApi = apiService.getInstance().core.sitesApi;
|
||||
spyOn(sitesApi, 'getSites').and.returnValue(Promise.resolve(fakeGetSitesAnswer));
|
||||
fixture.detectChanges();
|
||||
|
||||
documentList.loadFolderByNodeId('-sites-');
|
||||
expect(sitesApi.getSites).toHaveBeenCalled();
|
||||
|
||||
documentList.ready.subscribe((page) => {
|
||||
let disposableReady = documentList.ready.subscribe((page) => {
|
||||
const wrongName = page.list.entries.filter(item => (item.entry.name !== item.entry.title));
|
||||
expect(wrongName.length).toBe(0);
|
||||
disposableReady.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
documentList.loadFolderByNodeId('-sites-');
|
||||
});
|
||||
|
||||
it('should fetch user membership sites', () => {
|
||||
const peopleApi = apiService.getInstance().core.peopleApi;
|
||||
spyOn(peopleApi, 'getSiteMembership').and.returnValue(Promise.resolve());
|
||||
spyOn(peopleApi, 'getSiteMembership').and.returnValue(Promise.resolve(fakeGetSiteMembership));
|
||||
|
||||
documentList.loadFolderByNodeId('-mysites-');
|
||||
expect(peopleApi.getSiteMembership).toHaveBeenCalled();
|
||||
@@ -1096,8 +1142,9 @@ describe('DocumentList', () => {
|
||||
spyOn(apiService.getInstance().core.peopleApi, 'getSiteMembership')
|
||||
.and.returnValue(Promise.reject('error'));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe('error');
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -1105,47 +1152,51 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should assure that user membership sites have name property set', (done) => {
|
||||
fixture.detectChanges();
|
||||
const peopleApi = apiService.getInstance().core.peopleApi;
|
||||
spyOn(peopleApi, 'getSiteMembership').and.returnValue(Promise.resolve(fakeGetSiteMembership));
|
||||
|
||||
documentList.loadFolderByNodeId('-mysites-');
|
||||
expect(peopleApi.getSiteMembership).toHaveBeenCalled();
|
||||
|
||||
documentList.ready.subscribe((page) => {
|
||||
let disposableReady = documentList.ready.subscribe((page) => {
|
||||
const entriesWithoutName = page.list.entries.filter(item => !item.entry.name);
|
||||
expect(entriesWithoutName.length).toBe(0);
|
||||
disposableReady.unsubscribe();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should assure that user membership sites have name property set correctly', (done) => {
|
||||
fixture.detectChanges();
|
||||
const peopleApi = apiService.getInstance().core.peopleApi;
|
||||
spyOn(peopleApi, 'getSiteMembership').and.returnValue(Promise.resolve(fakeGetSiteMembership));
|
||||
|
||||
documentList.loadFolderByNodeId('-mysites-');
|
||||
expect(peopleApi.getSiteMembership).toHaveBeenCalled();
|
||||
|
||||
documentList.ready.subscribe((page) => {
|
||||
let disposableReady = documentList.ready.subscribe((page) => {
|
||||
const wrongName = page.list.entries.filter(item => (item.entry.name !== item.entry.title));
|
||||
expect(wrongName.length).toBe(0);
|
||||
disposableReady.unsubscribe();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch favorites', () => {
|
||||
const favoritesApi = apiService.getInstance().core.favoritesApi;
|
||||
spyOn(favoritesApi, 'getFavorites').and.returnValue(Promise.resolve(null));
|
||||
spyFavorite.and.returnValue(Promise.resolve(null));
|
||||
|
||||
documentList.loadFolderByNodeId('-favorites-');
|
||||
expect(favoritesApi.getFavorites).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit error when fetch favorites fails', (done) => {
|
||||
spyOn(apiService.getInstance().core.favoritesApi, 'getFavorites')
|
||||
.and.returnValue(Promise.reject('error'));
|
||||
spyFavorite.and.returnValue(Promise.reject('error'));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe('error');
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -1165,19 +1216,21 @@ describe('DocumentList', () => {
|
||||
it('should emit error when fetch recent fails on getPerson call', (done) => {
|
||||
spyOn(apiService.peopleApi, 'getPerson').and.returnValue(Promise.reject('error'));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe('error');
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
documentList.loadFolderByNodeId('-recent-');
|
||||
});
|
||||
|
||||
it('should emit error when fetch recent fails on search call', (done) => {
|
||||
xit('should emit error when fetch recent fails on search call', (done) => {
|
||||
spyOn(customResourcesService, 'loadFolderByNodeId').and.returnValue(Observable.throw('error'));
|
||||
|
||||
documentList.error.subscribe(val => {
|
||||
let disposableError = documentList.error.subscribe(val => {
|
||||
expect(val).toBe('error');
|
||||
disposableError.unsubscribe();
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -1195,9 +1248,6 @@ describe('DocumentList', () => {
|
||||
it('should reset folder node on loading folder by node id', () => {
|
||||
documentList.folderNode = <any> {};
|
||||
|
||||
const sitesApi = apiService.getInstance().core.sitesApi;
|
||||
spyOn(sitesApi, 'getSites').and.returnValue(Promise.resolve(null));
|
||||
|
||||
documentList.loadFolderByNodeId('-sites-');
|
||||
|
||||
expect(documentList.folderNode).toBeNull();
|
||||
@@ -1207,7 +1257,7 @@ describe('DocumentList', () => {
|
||||
documentList.currentFolderId = '12345-some-id-6789';
|
||||
|
||||
const peopleApi = apiService.getInstance().core.peopleApi;
|
||||
spyOn(peopleApi, 'getSiteMembership').and.returnValue(Promise.resolve());
|
||||
spyOn(peopleApi, 'getSiteMembership').and.returnValue(Promise.resolve(fakeGetSiteMembership));
|
||||
|
||||
documentList.loadFolderByNodeId('-mysites-');
|
||||
expect(documentList.currentFolderId).toBe('-mysites-');
|
||||
@@ -1263,9 +1313,10 @@ describe('DocumentList', () => {
|
||||
});
|
||||
|
||||
it('should add includeFields in the server request when present', () => {
|
||||
fixture.detectChanges();
|
||||
documentList.currentFolderId = 'fake-id';
|
||||
documentList.includeFields = ['test-include'];
|
||||
spyOn(documentListService, 'getFolder');
|
||||
spyOn(documentListService, 'getFolder').and.stub();
|
||||
|
||||
documentList.ngOnChanges({ rowFilter: new SimpleChange(null, <RowFilter> {}, true) });
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
import {
|
||||
ContentService, DataCellEvent, DataColumn, DataRowActionEvent, DataSorting, DataTableComponent,
|
||||
DisplayMode, ObjectDataColumn, PaginatedComponent, AppConfigService, DataColumnListComponent,
|
||||
UserPreferencesService, PaginationModel
|
||||
UserPreferencesService, PaginationModel, ThumbnailService
|
||||
} from '@alfresco/adf-core';
|
||||
|
||||
import { MinimalNodeEntity, MinimalNodeEntryEntity, NodePaging } from 'alfresco-js-api';
|
||||
@@ -136,7 +136,11 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
* override the default sorting detected by the component based on columns.
|
||||
*/
|
||||
@Input()
|
||||
sorting: string[];
|
||||
sorting = ['name', 'asc'];
|
||||
|
||||
/** Defines sorting mode. Can be either `client` or `server`. */
|
||||
@Input()
|
||||
sortingMode = 'client';
|
||||
|
||||
/** The inline style to apply to every row. See
|
||||
* the Angular NgStyle
|
||||
@@ -231,8 +235,8 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
|
||||
private _pagination: BehaviorSubject<PaginationModel>;
|
||||
private layoutPresets = {};
|
||||
|
||||
private contextActionHandlerSubscription: Subscription;
|
||||
private subscriptions: Subscription[] = [];
|
||||
private rowMenuCache: { [key: string]: ContentActionModel[] } = {};
|
||||
|
||||
constructor(private documentListService: DocumentListService,
|
||||
private ngZone: NgZone,
|
||||
@@ -240,7 +244,8 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
private appConfig: AppConfigService,
|
||||
private preferences: UserPreferencesService,
|
||||
private customResourcesService: CustomResourcesService,
|
||||
private contentService: ContentService) {
|
||||
private contentService: ContentService,
|
||||
private thumbnailService: ThumbnailService) {
|
||||
}
|
||||
|
||||
getContextActions(node: MinimalNodeEntity) {
|
||||
@@ -329,8 +334,9 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.rowMenuCache = {};
|
||||
this.loadLayoutPresets();
|
||||
this.data = new ShareDataTableAdapter(this.documentListService, null, this.getDefaultSorting());
|
||||
this.data = new ShareDataTableAdapter(this.documentListService, this.thumbnailService, null, this.getDefaultSorting(), this.sortingMode);
|
||||
this.data.thumbnails = this.thumbnails;
|
||||
this.data.permissionsStyle = this.permissionsStyle;
|
||||
|
||||
@@ -342,12 +348,25 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
this.data.setImageResolver(this.imageResolver);
|
||||
}
|
||||
|
||||
this.contextActionHandlerSubscription = this.contextActionHandler.subscribe(val => this.contextActionCallback(val));
|
||||
this.subscriptions.push(
|
||||
this.contextActionHandler.subscribe(val => this.contextActionCallback(val))
|
||||
);
|
||||
|
||||
this.enforceSingleClickNavigationForMobile();
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
if (this.columnList) {
|
||||
this.subscriptions.push(
|
||||
this.columnList.columns.changes.subscribe(() => {
|
||||
this.setTableSchema();
|
||||
})
|
||||
);
|
||||
}
|
||||
this.setTableSchema();
|
||||
}
|
||||
|
||||
private setTableSchema() {
|
||||
let schema: DataColumn[] = [];
|
||||
|
||||
if (this.hasCustomLayout) {
|
||||
@@ -355,7 +374,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
}
|
||||
|
||||
if (!this.data) {
|
||||
this.data = new ShareDataTableAdapter(this.documentListService, schema, this.getDefaultSorting());
|
||||
this.data = new ShareDataTableAdapter(this.documentListService, this.thumbnailService, schema, this.getDefaultSorting(), this.sortingMode);
|
||||
} else if (schema && schema.length > 0) {
|
||||
this.data.setColumns(schema);
|
||||
}
|
||||
@@ -368,6 +387,21 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.resetSelection();
|
||||
if (this.data) {
|
||||
this.data.thumbnails = this.thumbnails;
|
||||
|
||||
}
|
||||
if (changes.sortingMode && !changes.sortingMode.firstChange && this.data) {
|
||||
this.data.sortingMode = changes.sortingMode.currentValue;
|
||||
}
|
||||
|
||||
if (changes.sorting && !changes.sorting.firstChange && this.data) {
|
||||
const newValue = changes.sorting.currentValue;
|
||||
if (newValue && newValue.length > 0) {
|
||||
const [key, direction] = newValue;
|
||||
this.data.setSorting(new DataSorting(key, direction));
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.folderNode && changes.folderNode.currentValue) {
|
||||
this.currentFolderId = changes.folderNode.currentValue.id;
|
||||
@@ -380,9 +414,6 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
this.loadFolder();
|
||||
} else if (this.data) {
|
||||
if (changes.node && changes.node.currentValue) {
|
||||
if (changes.node.currentValue.list.pagination) {
|
||||
changes.node.currentValue.list.pagination.skipCount = 0;
|
||||
}
|
||||
this.data.loadPage(changes.node.currentValue);
|
||||
this.onDataReady(changes.node.currentValue);
|
||||
} else if (changes.rowFilter) {
|
||||
@@ -415,9 +446,10 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
}
|
||||
|
||||
getNodeActions(node: MinimalNodeEntity | any): ContentActionModel[] {
|
||||
let target = null;
|
||||
|
||||
if (node && node.entry) {
|
||||
let target = null;
|
||||
|
||||
if (node.entry.isFile) {
|
||||
target = 'document';
|
||||
} else if (node.entry.isFolder) {
|
||||
@@ -425,14 +457,29 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
}
|
||||
|
||||
if (target) {
|
||||
let actionsByTarget = this.actions.filter(entry => {
|
||||
return entry.target.toLowerCase() === target;
|
||||
}).map(action => new ContentActionModel(action));
|
||||
const actions = this.rowMenuCache[node.entry.id];
|
||||
if (actions) {
|
||||
actions.forEach(action => {
|
||||
this.refreshAction(action, node);
|
||||
});
|
||||
return actions;
|
||||
}
|
||||
|
||||
let actionsByTarget = this.actions
|
||||
.filter(entry => {
|
||||
const isVisible = (typeof entry.visible === 'function')
|
||||
? entry.visible(node)
|
||||
: entry.visible;
|
||||
|
||||
return isVisible && entry.target.toLowerCase() === target;
|
||||
})
|
||||
.map(action => new ContentActionModel(action));
|
||||
|
||||
actionsByTarget.forEach((action) => {
|
||||
this.disableActionsWithNoPermissions(node, action);
|
||||
this.refreshAction(action, node);
|
||||
});
|
||||
|
||||
this.rowMenuCache[node.entry.id] = actionsByTarget;
|
||||
return actionsByTarget;
|
||||
}
|
||||
}
|
||||
@@ -440,10 +487,29 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
return [];
|
||||
}
|
||||
|
||||
disableActionsWithNoPermissions(node: MinimalNodeEntity, action: ContentActionModel) {
|
||||
if (action.permission && node.entry.allowableOperations && !this.contentService.hasPermission(node.entry, action.permission)) {
|
||||
action.disabled = true;
|
||||
private refreshAction(action: ContentActionModel, node: MinimalNodeEntity) {
|
||||
action.disabled = this.isActionDisabled(action, node);
|
||||
action.visible = this.isActionVisible(action, node);
|
||||
}
|
||||
|
||||
private isActionVisible(action: ContentActionModel, node: MinimalNodeEntity): boolean {
|
||||
if (typeof action.visible === 'function') {
|
||||
return action.visible(node);
|
||||
}
|
||||
|
||||
return action.visible;
|
||||
}
|
||||
|
||||
private isActionDisabled(action: ContentActionModel, node: MinimalNodeEntity): boolean {
|
||||
if (typeof action.disabled === 'function') {
|
||||
return action.disabled(node);
|
||||
}
|
||||
|
||||
if (action.permission && action.disableWithNoPermission && !this.contentService.hasPermission(node.entry, action.permission)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return action.disabled;
|
||||
}
|
||||
|
||||
@HostListener('contextmenu', ['$event'])
|
||||
@@ -570,7 +636,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
this.noPermission = false;
|
||||
}
|
||||
|
||||
private onPageLoaded(nodePaging: NodePaging) {
|
||||
onPageLoaded(nodePaging: NodePaging) {
|
||||
if (nodePaging) {
|
||||
this.data.loadPage(nodePaging, this.pagination.getValue().merge);
|
||||
this.loading = false;
|
||||
@@ -738,6 +804,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
}
|
||||
|
||||
updatePagination(pagination: PaginationModel) {
|
||||
this.pagination.next(pagination);
|
||||
this.reload();
|
||||
}
|
||||
|
||||
@@ -770,10 +837,8 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.contextActionHandlerSubscription) {
|
||||
this.contextActionHandlerSubscription.unsubscribe();
|
||||
this.contextActionHandlerSubscription = null;
|
||||
}
|
||||
this.subscriptions.forEach(s => s.unsubscribe());
|
||||
this.subscriptions = [];
|
||||
}
|
||||
|
||||
private handleError(err: any) {
|
||||
|
||||
+7
-22
@@ -16,36 +16,21 @@
|
||||
*/
|
||||
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { DataTableComponent, DataTableModule } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from '../../services/document-list.service';
|
||||
import { CustomResourcesService } from '../../services/custom-resources.service';
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { DataTableComponent, setupTestBed } from '@alfresco/adf-core';
|
||||
import { DocumentListComponent } from './../document-list.component';
|
||||
import { EmptyFolderContentDirective } from './empty-folder-content.directive';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('EmptyFolderContent', () => {
|
||||
|
||||
let emptyFolderContent: EmptyFolderContentDirective;
|
||||
let documentList: DocumentListComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent
|
||||
],
|
||||
providers: [
|
||||
DocumentListService,
|
||||
CustomResourcesService
|
||||
],
|
||||
schemas: [
|
||||
CUSTOM_ELEMENTS_SCHEMA
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
documentList = (TestBed.createComponent(DocumentListComponent).componentInstance as DocumentListComponent);
|
||||
|
||||
+6
-21
@@ -15,35 +15,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { MatProgressSpinnerModule } from '@angular/material';
|
||||
import { DataTableComponent, DataTableModule } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from '../../services/document-list.service';
|
||||
import { CustomResourcesService } from '../../services/custom-resources.service';
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { DataTableComponent, setupTestBed } from '@alfresco/adf-core';
|
||||
import { DocumentListComponent } from './../document-list.component';
|
||||
import { NoPermissionContentDirective } from './no-permission-content.directive';
|
||||
import { ContentTestingModule } from '../../../testing/content.testing.module';
|
||||
|
||||
describe('NoPermissionContentDirective', () => {
|
||||
|
||||
let noPermissionContent: NoPermissionContentDirective;
|
||||
let documentList: DocumentListComponent;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
DataTableModule,
|
||||
MatProgressSpinnerModule
|
||||
],
|
||||
declarations: [
|
||||
DocumentListComponent
|
||||
],
|
||||
providers: [
|
||||
DocumentListService,
|
||||
CustomResourcesService
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [ContentTestingModule]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
documentList = (TestBed.createComponent(DocumentListComponent).componentInstance as DocumentListComponent);
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DataRow } from '@alfresco/adf-core';
|
||||
import { ObjectUtils } from '@alfresco/adf-core';
|
||||
import { DataRow, ObjectUtils, ThumbnailService } from '@alfresco/adf-core';
|
||||
import { MinimalNode, MinimalNodeEntity } from 'alfresco-js-api';
|
||||
import { PermissionStyleModel } from './../models/permissions-style.model';
|
||||
import { DocumentListService } from './../services/document-list.service';
|
||||
@@ -34,7 +33,10 @@ export class ShareDataRow implements DataRow {
|
||||
return this.obj;
|
||||
}
|
||||
|
||||
constructor(private obj: MinimalNodeEntity, private documentListService: DocumentListService, private permissionsStyle: PermissionStyleModel[]) {
|
||||
constructor(private obj: MinimalNodeEntity,
|
||||
private documentListService: DocumentListService,
|
||||
private permissionsStyle: PermissionStyleModel[],
|
||||
private thumbnailService?: ThumbnailService) {
|
||||
if (!obj) {
|
||||
throw new Error(ShareDataRow.ERR_OBJECT_NOT_FOUND);
|
||||
}
|
||||
@@ -91,6 +93,10 @@ export class ShareDataRow implements DataRow {
|
||||
return ObjectUtils.getValue(this.obj.entry, key);
|
||||
}
|
||||
|
||||
imageErrorResolver(event: Event): any {
|
||||
return this.thumbnailService.getMimeTypeIcon(this.obj.entry.content.mimeType);
|
||||
}
|
||||
|
||||
hasValue(key: string): boolean {
|
||||
return this.getValue(key) !== undefined;
|
||||
}
|
||||
|
||||
@@ -32,30 +32,54 @@ describe('ShareDataTableAdapter', () => {
|
||||
spyOn(documentListService, 'getDocumentThumbnailUrl').and.returnValue(imageUrl);
|
||||
});
|
||||
|
||||
it('should use client sorting by default', () => {
|
||||
const adapter = new ShareDataTableAdapter(documentListService, null, []);
|
||||
expect(adapter.sortingMode).toBe('client');
|
||||
});
|
||||
|
||||
it('should not be case sensitive for sorting mode value', () => {
|
||||
const adapter = new ShareDataTableAdapter(documentListService, null, []);
|
||||
|
||||
adapter.sortingMode = 'CLIENT';
|
||||
expect(adapter.sortingMode).toBe('client');
|
||||
|
||||
adapter.sortingMode = 'SeRvEr';
|
||||
expect(adapter.sortingMode).toBe('server');
|
||||
});
|
||||
|
||||
it('should fallback to client sorting for unknown values', () => {
|
||||
const adapter = new ShareDataTableAdapter(documentListService, null, []);
|
||||
|
||||
adapter.sortingMode = 'SeRvEr';
|
||||
expect(adapter.sortingMode).toBe('server');
|
||||
|
||||
adapter.sortingMode = 'quantum';
|
||||
expect(adapter.sortingMode).toBe('client');
|
||||
});
|
||||
|
||||
it('should setup rows and columns with constructor', () => {
|
||||
let schema = [<DataColumn> {}];
|
||||
let adapter = new ShareDataTableAdapter(documentListService, schema);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, schema);
|
||||
|
||||
expect(adapter.getRows()).toEqual([]);
|
||||
expect(adapter.getColumns()).toEqual(schema);
|
||||
});
|
||||
|
||||
it('should setup columns when constructor is missing schema', () => {
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
|
||||
const adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
expect(adapter.getColumns()).toEqual([]);
|
||||
});
|
||||
|
||||
it('should set new columns', () => {
|
||||
let columns = [<DataColumn> {}, <DataColumn> {}];
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
adapter.setColumns(columns);
|
||||
expect(adapter.getColumns()).toEqual(columns);
|
||||
});
|
||||
|
||||
it('should reset columns', () => {
|
||||
let columns = [<DataColumn> {}, <DataColumn> {}];
|
||||
let adapter = new ShareDataTableAdapter(documentListService, columns);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, columns);
|
||||
|
||||
expect(adapter.getColumns()).toEqual(columns);
|
||||
adapter.setColumns(null);
|
||||
@@ -64,7 +88,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
|
||||
it('should set new rows', () => {
|
||||
let rows = [<DataRow> {}, <DataRow> {}];
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
|
||||
expect(adapter.getRows()).toEqual([]);
|
||||
adapter.setRows(rows);
|
||||
@@ -73,7 +97,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
|
||||
it('should reset rows', () => {
|
||||
let rows = [<DataRow> {}, <DataRow> {}];
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
|
||||
adapter.setRows(rows);
|
||||
expect(adapter.getRows()).toEqual(rows);
|
||||
@@ -83,7 +107,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
});
|
||||
|
||||
it('should sort new rows', () => {
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
spyOn(adapter, 'sort').and.callThrough();
|
||||
|
||||
let rows = [<DataRow> {}];
|
||||
@@ -93,7 +117,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
});
|
||||
|
||||
it('should fail when getting value for missing row', () => {
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
let check = () => {
|
||||
return adapter.getValue(null, <DataColumn> {});
|
||||
};
|
||||
@@ -101,53 +125,13 @@ describe('ShareDataTableAdapter', () => {
|
||||
});
|
||||
|
||||
it('should fail when getting value for missing column', () => {
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
let check = () => {
|
||||
return adapter.getValue(<DataRow> {}, null);
|
||||
};
|
||||
expect(check).toThrowError(adapter.ERR_COL_NOT_FOUND);
|
||||
});
|
||||
|
||||
it('should covert cell value to formatted date', () => {
|
||||
let rawValue = new Date(2015, 6, 15, 21, 43, 11); // Wed Jul 15 2015 21:43:11 GMT+0100 (BST);
|
||||
let dateValue = 'Jul 15, 2015, 9:43:11 PM';
|
||||
|
||||
let file = new FileNode();
|
||||
file.entry.createdAt = rawValue;
|
||||
|
||||
let col = <DataColumn> {
|
||||
key: 'createdAt',
|
||||
type: 'date',
|
||||
format: 'medium' // Jul 15, 2015, 9:43:11 PM
|
||||
};
|
||||
|
||||
let row = new ShareDataRow(file, documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
|
||||
let value = adapter.getValue(row, col);
|
||||
expect(value).toBe(dateValue);
|
||||
});
|
||||
|
||||
it('should use default date format as fallback', () => {
|
||||
let rawValue = new Date(2015, 6, 15, 21, 43, 11); // Wed Jul 15 2015 21:43:11 GMT+0100 (BST);
|
||||
let dateValue = 'Jul 15, 2015, 9:43:11 PM';
|
||||
|
||||
let file = new FileNode();
|
||||
file.entry.createdAt = rawValue;
|
||||
|
||||
let col = <DataColumn> {
|
||||
key: 'createdAt',
|
||||
type: 'date',
|
||||
format: null
|
||||
};
|
||||
|
||||
let row = new ShareDataRow(file, documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
|
||||
let value = adapter.getValue(row, col);
|
||||
expect(value).toBe(dateValue);
|
||||
});
|
||||
|
||||
it('should return date value as string', () => {
|
||||
let rawValue = new Date(2015, 6, 15, 21, 43, 11); // Wed Jul 15 2015 21:43:11 GMT+0100 (BST);
|
||||
|
||||
@@ -160,36 +144,16 @@ describe('ShareDataTableAdapter', () => {
|
||||
};
|
||||
|
||||
let row = new ShareDataRow(file, documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
|
||||
let value = adapter.getValue(row, col);
|
||||
expect(value).toBe(rawValue);
|
||||
});
|
||||
|
||||
it('should log error when having date conversion issues', () => {
|
||||
let dateValue = <Date> {};
|
||||
let file = new FileNode();
|
||||
file.entry.createdAt = <any> dateValue;
|
||||
|
||||
let col = <DataColumn> {
|
||||
key: 'createdAt',
|
||||
type: 'date',
|
||||
format: 'medium'
|
||||
};
|
||||
|
||||
let row = new ShareDataRow(file, documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
spyOn(console, 'error').and.stub();
|
||||
|
||||
let value = adapter.getValue(row, col);
|
||||
expect(value).toBe('Error');
|
||||
expect(console.error).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should generate fallback icon for a file thumbnail with missing mime type', () => {
|
||||
spyOn(documentListService, 'getDefaultMimeTypeIcon').and.returnValue(`assets/images/ft_ic_miscellaneouse.svg`);
|
||||
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
|
||||
let file = new FileNode();
|
||||
file.entry.content.mimeType = null;
|
||||
@@ -205,7 +169,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
it('should generate fallback icon for a file with no content entry', () => {
|
||||
spyOn(documentListService, 'getDefaultMimeTypeIcon').and.returnValue(`assets/images/ft_ic_miscellaneouse.svg`);
|
||||
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
|
||||
let file = new FileNode();
|
||||
file.entry.content = null;
|
||||
@@ -224,7 +188,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
let file = new FileNode();
|
||||
file.entry['icon'] = imageUrl;
|
||||
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
let row = new ShareDataRow(file, documentListService, null);
|
||||
let col = <DataColumn> { type: 'image', key: 'icon' };
|
||||
|
||||
@@ -235,7 +199,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
it('should resolve folder icon', () => {
|
||||
spyOn(documentListService, 'getMimeTypeIcon').and.returnValue(`assets/images/ft_ic_folder.svg`);
|
||||
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
|
||||
let row = new ShareDataRow(new FolderNode(), documentListService, null);
|
||||
let col = <DataColumn> { type: 'image', key: '$thumbnail' };
|
||||
@@ -247,7 +211,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
|
||||
it('should resolve file thumbnail', () => {
|
||||
let imageUrl = 'http://<addresss>';
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
adapter.thumbnails = true;
|
||||
|
||||
let file = new FileNode();
|
||||
@@ -262,7 +226,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
it('should resolve fallback file icon for unknown node', () => {
|
||||
spyOn(documentListService, 'getDefaultMimeTypeIcon').and.returnValue(`assets/images/ft_ic_miscellaneous.svg`);
|
||||
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
|
||||
let file = new FileNode();
|
||||
file.entry.isFile = false;
|
||||
@@ -279,7 +243,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
|
||||
it('should resolve file icon for content type', () => {
|
||||
spyOn(documentListService, 'getMimeTypeIcon').and.returnValue(`assets/images/ft_ic_raster_image.svg`);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, null);
|
||||
|
||||
let file = new FileNode();
|
||||
file.entry.isFile = false;
|
||||
@@ -300,7 +264,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
let folder = new FolderNode();
|
||||
|
||||
let col = <DataColumn> { key: 'name' };
|
||||
let adapter = new ShareDataTableAdapter(documentListService, [col]);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, [col]);
|
||||
adapter.setSorting(new DataSorting('name', 'asc'));
|
||||
|
||||
adapter.setRows([
|
||||
@@ -323,7 +287,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
file2.entry['dateProp'] = new Date(2016, 6, 30, 13, 14, 2);
|
||||
|
||||
let col = <DataColumn> { key: 'dateProp' };
|
||||
let adapter = new ShareDataTableAdapter(documentListService, [col]);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, [col]);
|
||||
|
||||
adapter.setRows([
|
||||
new ShareDataRow(file2, documentListService, null),
|
||||
@@ -353,7 +317,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
file4.entry.content.sizeInBytes = 2852791665; // 2.66 GB
|
||||
|
||||
let col = <DataColumn> { key: 'content.sizeInBytes' };
|
||||
let adapter = new ShareDataTableAdapter(documentListService, [col]);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, [col]);
|
||||
|
||||
adapter.setRows([
|
||||
new ShareDataRow(file3, documentListService, null),
|
||||
@@ -386,7 +350,7 @@ describe('ShareDataTableAdapter', () => {
|
||||
let file6 = new FileNode('b');
|
||||
|
||||
let col = <DataColumn> { key: 'name' };
|
||||
let adapter = new ShareDataTableAdapter(documentListService, [col]);
|
||||
let adapter = new ShareDataTableAdapter(documentListService, null, [col]);
|
||||
|
||||
adapter.setRows([
|
||||
new ShareDataRow(file4, documentListService, null),
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DataColumn, DataRow, DataSorting, DataTableAdapter } from '@alfresco/adf-core';
|
||||
import { TimeAgoPipe } from '@alfresco/adf-core';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { DataColumn, DataRow, DataSorting, DataTableAdapter, ThumbnailService } from '@alfresco/adf-core';
|
||||
import { NodePaging } from 'alfresco-js-api';
|
||||
import { PermissionStyleModel } from './../models/permissions-style.model';
|
||||
import { DocumentListService } from './../services/document-list.service';
|
||||
@@ -28,6 +26,7 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
ERR_ROW_NOT_FOUND: string = 'Row not found';
|
||||
ERR_COL_NOT_FOUND: string = 'Column not found';
|
||||
|
||||
private _sortingMode: string;
|
||||
private sorting: DataSorting;
|
||||
private rows: DataRow[];
|
||||
private columns: DataColumn[];
|
||||
@@ -39,12 +38,27 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
permissionsStyle: PermissionStyleModel[];
|
||||
selectedRow: DataRow;
|
||||
|
||||
set sortingMode(value: string) {
|
||||
let newValue = (value || 'client').toLowerCase();
|
||||
if (newValue !== 'client' && newValue !== 'server') {
|
||||
newValue = 'client';
|
||||
}
|
||||
this._sortingMode = newValue;
|
||||
}
|
||||
|
||||
get sortingMode(): string {
|
||||
return this._sortingMode;
|
||||
}
|
||||
|
||||
constructor(private documentListService: DocumentListService,
|
||||
private thumbnailService: ThumbnailService,
|
||||
schema: DataColumn[] = [],
|
||||
sorting?: DataSorting) {
|
||||
sorting?: DataSorting,
|
||||
sortingMode: string = 'client') {
|
||||
this.rows = [];
|
||||
this.columns = schema || [];
|
||||
this.sorting = sorting;
|
||||
this.sortingMode = sortingMode;
|
||||
}
|
||||
|
||||
getRows(): Array<DataRow> {
|
||||
@@ -78,16 +92,6 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
return dataRow.cache[col.key];
|
||||
}
|
||||
|
||||
if (col.type === 'date') {
|
||||
try {
|
||||
const result = this.formatDate(col, value);
|
||||
return dataRow.cacheValue(col.key, result);
|
||||
} catch (err) {
|
||||
console.error(`Error parsing date ${value} to format ${col.format}`);
|
||||
return 'Error';
|
||||
}
|
||||
}
|
||||
|
||||
if (col.key === '$thumbnail') {
|
||||
|
||||
if (this.imageResolver) {
|
||||
@@ -132,21 +136,6 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
return dataRow.cacheValue(col.key, value);
|
||||
}
|
||||
|
||||
formatDate(col: DataColumn, value: any): string {
|
||||
if (col.type === 'date') {
|
||||
const format = col.format || 'medium';
|
||||
if (format === 'timeAgo') {
|
||||
const timeAgoPipe = new TimeAgoPipe();
|
||||
return timeAgoPipe.transform(value);
|
||||
} else {
|
||||
const datePipe = new DatePipe('en-US');
|
||||
return datePipe.transform(value, format);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
getSorting(): DataSorting {
|
||||
return this.sorting;
|
||||
}
|
||||
@@ -175,6 +164,10 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
}
|
||||
|
||||
private sortRows(rows: DataRow[], sorting: DataSorting) {
|
||||
if (this.sortingMode === 'server') {
|
||||
return;
|
||||
}
|
||||
|
||||
const options: Intl.CollatorOptions = {};
|
||||
|
||||
if (sorting && sorting.key && rows && rows.length > 0) {
|
||||
@@ -215,23 +208,25 @@ export class ShareDataTableAdapter implements DataTableAdapter {
|
||||
if (page && page.list) {
|
||||
let data = page.list.entries;
|
||||
if (data && data.length > 0) {
|
||||
rows = data.map(item => new ShareDataRow(item, this.documentListService, this.permissionsStyle));
|
||||
rows = data.map(item => new ShareDataRow(item, this.documentListService, this.permissionsStyle, this.thumbnailService));
|
||||
|
||||
if (this.filter) {
|
||||
rows = rows.filter(this.filter);
|
||||
}
|
||||
|
||||
// Sort by first sortable or just first column
|
||||
if (this.columns && this.columns.length > 0) {
|
||||
let sorting = this.getSorting();
|
||||
if (sorting) {
|
||||
this.sortRows(rows, sorting);
|
||||
} else {
|
||||
let sortable = this.columns.filter(c => c.sortable);
|
||||
if (sortable.length > 0) {
|
||||
this.sort(sortable[0].key, 'asc');
|
||||
if (this.sortingMode !== 'server') {
|
||||
// Sort by first sortable or just first column
|
||||
if (this.columns && this.columns.length > 0) {
|
||||
let sorting = this.getSorting();
|
||||
if (sorting) {
|
||||
this.sortRows(rows, sorting);
|
||||
} else {
|
||||
this.sort(this.columns[0].key, 'asc');
|
||||
let sortable = this.columns.filter(c => c.sortable);
|
||||
if (sortable.length > 0) {
|
||||
this.sort(sortable[0].key, 'asc');
|
||||
} else {
|
||||
this.sort(this.columns[0].key, 'asc');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ export class ContentActionModel {
|
||||
target: string;
|
||||
permission: string;
|
||||
disableWithNoPermission: boolean = false;
|
||||
disabled: boolean = false;
|
||||
disabled: boolean | Function = false;
|
||||
visible: boolean | Function = true;
|
||||
|
||||
constructor(obj?: any) {
|
||||
if (obj) {
|
||||
@@ -34,7 +35,14 @@ export class ContentActionModel {
|
||||
this.target = obj.target;
|
||||
this.permission = obj.permission;
|
||||
this.disableWithNoPermission = obj.disableWithNoPermission;
|
||||
this.disabled = obj.disabled;
|
||||
|
||||
if (obj.hasOwnProperty('disabled')) {
|
||||
this.disabled = obj.disabled;
|
||||
}
|
||||
|
||||
if (obj.hasOwnProperty('visible')) {
|
||||
this.visible = obj.visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ import {
|
||||
NodePaging,
|
||||
PersonEntry,
|
||||
SitePaging,
|
||||
DeletedNodesPaging
|
||||
DeletedNodesPaging,
|
||||
SearchRequest
|
||||
} from 'alfresco-js-api';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
@@ -39,12 +40,18 @@ export class CustomResourcesService {
|
||||
private logService: LogService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets files recently accessed by a user.
|
||||
* @param personId ID of the user
|
||||
* @param pagination Specifies how to paginate the results
|
||||
* @returns List of nodes for the recently used files
|
||||
*/
|
||||
getRecentFiles(personId: string, pagination: PaginationModel): Observable<NodePaging> {
|
||||
return new Observable(observer => {
|
||||
this.apiService.peopleApi.getPerson(personId)
|
||||
.then((person: PersonEntry) => {
|
||||
const username = person.entry.id;
|
||||
const query = {
|
||||
const query: SearchRequest = {
|
||||
query: {
|
||||
query: '*',
|
||||
language: 'afts'
|
||||
@@ -66,8 +73,8 @@ export class CustomResourcesService {
|
||||
}
|
||||
};
|
||||
return this.apiService.searchApi.search(query)
|
||||
.then((serachResult) => {
|
||||
observer.next(serachResult);
|
||||
.then((searchResult) => {
|
||||
observer.next(searchResult);
|
||||
observer.complete();
|
||||
},
|
||||
(err) => {
|
||||
@@ -82,6 +89,12 @@ export class CustomResourcesService {
|
||||
}).catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets favorite files for the current user.
|
||||
* @param pagination Specifies how to paginate the results
|
||||
* @param includeFields List of data field names to include in the results
|
||||
* @returns List of favorite files
|
||||
*/
|
||||
loadFavorites(pagination: PaginationModel, includeFields: string[] = []): Observable<NodePaging> {
|
||||
let includeFieldsRequest = this.getIncludesFields(includeFields);
|
||||
|
||||
@@ -122,6 +135,11 @@ export class CustomResourcesService {
|
||||
}).catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets sites that the current user is a member of.
|
||||
* @param pagination Specifies how to paginate the results
|
||||
* @returns List of sites
|
||||
*/
|
||||
loadMemberSites(pagination: PaginationModel): Observable<NodePaging> {
|
||||
const options = {
|
||||
include: ['properties'],
|
||||
@@ -156,6 +174,11 @@ export class CustomResourcesService {
|
||||
}).catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all sites in the respository.
|
||||
* @param pagination Specifies how to paginate the results
|
||||
* @returns List of sites
|
||||
*/
|
||||
loadSites(pagination: PaginationModel): Observable<NodePaging> {
|
||||
const options = {
|
||||
include: ['properties'],
|
||||
@@ -182,6 +205,12 @@ export class CustomResourcesService {
|
||||
}).catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all items currently in the trash.
|
||||
* @param pagination Specifies how to paginate the results
|
||||
* @param includeFields List of data field names to include in the results
|
||||
* @returns List of deleted items
|
||||
*/
|
||||
loadTrashcan(pagination: PaginationModel, includeFields: string[] = []): Observable<DeletedNodesPaging> {
|
||||
let includeFieldsRequest = this.getIncludesFields(includeFields);
|
||||
|
||||
@@ -195,6 +224,12 @@ export class CustomResourcesService {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets shared links for the current user.
|
||||
* @param pagination Specifies how to paginate the results
|
||||
* @param includeFields List of data field names to include in the results
|
||||
* @returns List of shared links
|
||||
*/
|
||||
loadSharedLinks(pagination: PaginationModel, includeFields: string[] = []): Observable<NodePaging> {
|
||||
let includeFieldsRequest = this.getIncludesFields(includeFields);
|
||||
|
||||
@@ -207,6 +242,11 @@ export class CustomResourcesService {
|
||||
return Observable.fromPromise(this.apiService.sharedLinksApi.findSharedLinks(options)).catch(err => this.handleError(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the folder ID one of the well-known aliases?
|
||||
* @param folderId Folder ID name to check
|
||||
* @returns True if the ID is a well-known name, false otherwise
|
||||
*/
|
||||
isCustomSource(folderId: string): boolean {
|
||||
let isCustomSources = false;
|
||||
const sources = ['-trashcan-', '-sharedlinks-', '-sites-', '-mysites-', '-favorites-', '-recent-'];
|
||||
@@ -218,6 +258,13 @@ export class CustomResourcesService {
|
||||
return isCustomSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a folder's contents.
|
||||
* @param nodeId ID of the target folder node
|
||||
* @param pagination Specifies how to paginate the results
|
||||
* @param includeFields List of data field names to include in the results
|
||||
* @returns List of items contained in the folder
|
||||
*/
|
||||
loadFolderByNodeId(nodeId: string, pagination: PaginationModel, includeFields: string[]): Observable<NodePaging> {
|
||||
if (nodeId === '-trashcan-') {
|
||||
return this.loadTrashcan(pagination, includeFields);
|
||||
@@ -235,6 +282,13 @@ export class CustomResourcesService {
|
||||
}
|
||||
|
||||
// TODO: remove it from here
|
||||
|
||||
/**
|
||||
* Gets the contents of one of the well-known aliases in the form of node ID strings.
|
||||
* @param nodeId ID of the target folder node
|
||||
* @param pagination Specifies how to paginate the results
|
||||
* @returns List of node IDs
|
||||
*/
|
||||
getCorrespondingNodeIds(nodeId: string, pagination: PaginationModel): Observable<string[]> {
|
||||
if (nodeId === '-trashcan-') {
|
||||
return Observable.fromPromise(this.apiService.nodesApi.getDeletedNodes()
|
||||
|
||||
@@ -15,12 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
AlfrescoApiServiceMock,
|
||||
AppConfigService,
|
||||
ContentService,
|
||||
StorageService,
|
||||
TranslationMock
|
||||
import { AlfrescoApiServiceMock, AppConfigService, ContentService,
|
||||
StorageService, setupTestBed, CoreModule, TranslationMock
|
||||
} from '@alfresco/adf-core';
|
||||
import { FileNode, FolderNode } from '../../mock';
|
||||
import { ContentActionHandler } from '../models/content-action.model';
|
||||
@@ -35,6 +31,12 @@ describe('DocumentActionsService', () => {
|
||||
let documentListService: DocumentListService;
|
||||
let nodeActionsService: NodeActionsService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
let contentService = new ContentService(null, null, null, null);
|
||||
let alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { AlfrescoApiServiceMock, AlfrescoApiService,
|
||||
AppConfigService, StorageService, ContentService } from '@alfresco/adf-core';
|
||||
AppConfigService, StorageService, ContentService, setupTestBed, CoreModule } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from './document-list.service';
|
||||
|
||||
declare let jasmine: any;
|
||||
@@ -87,6 +87,12 @@ describe('DocumentListService', () => {
|
||||
}
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
let contentService = new ContentService(null, null, null, null);
|
||||
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
|
||||
|
||||
@@ -90,7 +90,7 @@ export class DocumentListService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a node to destination node
|
||||
* Moves a node to destination node.
|
||||
*
|
||||
* @param nodeId The id of the node to be moved
|
||||
* @param targetParentId The id of the folder where the node will be moved
|
||||
@@ -102,7 +102,7 @@ export class DocumentListService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new folder in the path.
|
||||
* Creates a new folder in the path.
|
||||
* @param name Folder name
|
||||
* @param parentId Parent folder ID
|
||||
* @returns Details of the created folder node
|
||||
@@ -128,7 +128,7 @@ export class DocumentListService {
|
||||
|
||||
/**
|
||||
* Gets a node via its node ID.
|
||||
* @param nodeId
|
||||
* @param nodeId ID of the target node
|
||||
* @param includeFields Extra information to include (available options are "aspectNames", "isLink" and "association")
|
||||
* @returns Details of the folder
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { AlfrescoApiServiceMock, AppConfigService, StorageService, ContentService, TranslationMock } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiServiceMock, AppConfigService, StorageService, ContentService, setupTestBed, CoreModule, TranslationMock } from '@alfresco/adf-core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { FileNode, FolderNode } from '../../mock';
|
||||
import { ContentActionHandler } from '../models/content-action.model';
|
||||
@@ -28,6 +28,12 @@ describe('FolderActionsService', () => {
|
||||
let service: FolderActionsService;
|
||||
let documentListService: DocumentListService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
let appConfig: AppConfigService = TestBed.get(AppConfigService);
|
||||
appConfig.config.ecmHost = 'http://localhost:9876/ecm';
|
||||
|
||||
+18
-25
@@ -17,14 +17,13 @@
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { AppConfigService } from '@alfresco/adf-core';
|
||||
import { AppConfigService, setupTestBed, CoreModule } from '@alfresco/adf-core';
|
||||
import { DocumentListService } from './document-list.service';
|
||||
import { NodeActionsService } from './node-actions.service';
|
||||
import { ContentNodeDialogService } from '../../content-node-selector/content-node-dialog.service';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { MatDialogRef } from '@angular/material';
|
||||
import { NodeLockDialogComponent } from '../../dialogs/node-lock.dialog';
|
||||
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
|
||||
import { DialogModule } from '../../dialogs/dialog.module';
|
||||
|
||||
const fakeNode: MinimalNodeEntryEntity = <MinimalNodeEntryEntity> {
|
||||
id: 'fake'
|
||||
@@ -39,24 +38,18 @@ describe('NodeActionsService', () => {
|
||||
open: jasmine.createSpy('open')
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
NodeLockDialogComponent
|
||||
],
|
||||
providers: [
|
||||
NodeActionsService,
|
||||
DocumentListService,
|
||||
ContentNodeDialogService,
|
||||
{ provide: MatDialogRef, useValue: dialogRef }
|
||||
]
|
||||
});
|
||||
|
||||
TestBed.overrideModule(BrowserDynamicTestingModule, {
|
||||
set: { entryComponents: [ NodeLockDialogComponent ] }
|
||||
}).compileComponents();
|
||||
|
||||
}));
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot(),
|
||||
DialogModule
|
||||
],
|
||||
providers: [
|
||||
NodeActionsService,
|
||||
DocumentListService,
|
||||
ContentNodeDialogService,
|
||||
{ provide: MatDialogRef, useValue: dialogRef }
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
let appConfig: AppConfigService = TestBed.get(AppConfigService);
|
||||
@@ -76,7 +69,7 @@ describe('NodeActionsService', () => {
|
||||
spyOn(contentDialogService, 'openCopyMoveDialog').and.returnValue(Observable.of([fakeNode]));
|
||||
|
||||
service.copyContent(fakeNode, 'allowed').subscribe((value) => {
|
||||
expect(value).toBe('OPERATION.SUCCES.CONTENT.COPY');
|
||||
expect(value).toBe('OPERATION.SUCCESS.CONTENT.COPY');
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -85,7 +78,7 @@ describe('NodeActionsService', () => {
|
||||
spyOn(contentDialogService, 'openCopyMoveDialog').and.returnValue(Observable.of([fakeNode]));
|
||||
|
||||
service.moveContent(fakeNode, 'allowed').subscribe((value) => {
|
||||
expect(value).toBe('OPERATION.SUCCES.CONTENT.MOVE');
|
||||
expect(value).toBe('OPERATION.SUCCESS.CONTENT.MOVE');
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -94,7 +87,7 @@ describe('NodeActionsService', () => {
|
||||
spyOn(contentDialogService, 'openCopyMoveDialog').and.returnValue(Observable.of([fakeNode]));
|
||||
|
||||
service.moveFolder(fakeNode, 'allowed').subscribe((value) => {
|
||||
expect(value).toBe('OPERATION.SUCCES.FOLDER.MOVE');
|
||||
expect(value).toBe('OPERATION.SUCCESS.FOLDER.MOVE');
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -103,7 +96,7 @@ describe('NodeActionsService', () => {
|
||||
spyOn(contentDialogService, 'openCopyMoveDialog').and.returnValue(Observable.of([fakeNode]));
|
||||
|
||||
service.copyFolder(fakeNode, 'allowed').subscribe((value) => {
|
||||
expect(value).toBe('OPERATION.SUCCES.FOLDER.COPY');
|
||||
expect(value).toBe('OPERATION.SUCCESS.FOLDER.COPY');
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -100,7 +100,7 @@ export class NodeActionsService {
|
||||
const selection = selections[0];
|
||||
this.documentListService[`${action}Node`].call(this.documentListService, contentEntry.id, selection.id)
|
||||
.subscribe(
|
||||
observable.next.bind(observable, `OPERATION.SUCCES.${type.toUpperCase()}.${action.toUpperCase()}`),
|
||||
observable.next.bind(observable, `OPERATION.SUCCESS.${type.toUpperCase()}.${action.toUpperCase()}`),
|
||||
observable.error.bind(observable)
|
||||
);
|
||||
},
|
||||
|
||||
@@ -15,61 +15,70 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { Component } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatDialog, MatDialogModule } from '@angular/material';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { FolderDialogComponent } from '../dialogs/folder.dialog';
|
||||
|
||||
import { DirectiveModule, ContentService, TranslateLoaderService } from '@alfresco/adf-core';
|
||||
import { ContentService, setupTestBed, CoreModule } from '@alfresco/adf-core';
|
||||
import { FolderCreateDirective } from './folder-create.directive';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
|
||||
@Component({
|
||||
template: '<div [adf-create-folder]="parentNode"></div>'
|
||||
template: `
|
||||
<div
|
||||
[adf-create-folder]="parentNode"
|
||||
(success)="success($event)"
|
||||
title="create-title"
|
||||
[nodeType]="'cm:my-litte-pony'">
|
||||
</div>`
|
||||
})
|
||||
class TestTypeComponent {
|
||||
parentNode = '';
|
||||
public successParameter: MinimalNodeEntryEntity = null;
|
||||
|
||||
success(node: MinimalNodeEntryEntity) {
|
||||
this.successParameter = node;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `<div [adf-create-folder]="parentNode"></div>`
|
||||
})
|
||||
class TestComponent {
|
||||
parentNode = '';
|
||||
public successParameter: MinimalNodeEntryEntity = null;
|
||||
}
|
||||
|
||||
describe('FolderCreateDirective', () => {
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
let fixture: ComponentFixture<TestTypeComponent | TestComponent>;
|
||||
let element;
|
||||
let node: any;
|
||||
let dialog: MatDialog;
|
||||
let contentService: ContentService;
|
||||
let dialogRefMock;
|
||||
|
||||
const event = { type: 'click', preventDefault: () => null };
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
],
|
||||
declarations: [
|
||||
TestTypeComponent,
|
||||
TestComponent,
|
||||
FolderDialogComponent,
|
||||
FolderCreateDirective
|
||||
],
|
||||
providers: [
|
||||
ContentService
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
MatDialogModule,
|
||||
FormsModule,
|
||||
DirectiveModule,
|
||||
ReactiveFormsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderService
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [
|
||||
TestComponent,
|
||||
FolderDialogComponent,
|
||||
FolderCreateDirective
|
||||
],
|
||||
providers: [
|
||||
ContentService
|
||||
]
|
||||
});
|
||||
|
||||
TestBed.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
element = fixture.debugElement.query(By.directive(FolderCreateDirective));
|
||||
dialog = TestBed.get(MatDialog);
|
||||
@@ -80,37 +89,101 @@ describe('FolderCreateDirective', () => {
|
||||
node = { entry: { id: 'nodeId' } };
|
||||
|
||||
dialogRefMock = {
|
||||
afterClosed: val => Observable.of(val)
|
||||
afterClosed: val => Observable.of(val),
|
||||
componentInstance: {
|
||||
error: new Subject<any>(),
|
||||
success: new Subject<MinimalNodeEntryEntity>()
|
||||
}
|
||||
};
|
||||
|
||||
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
|
||||
});
|
||||
|
||||
xit('should emit folderCreate event when input value is not undefined', (done) => {
|
||||
spyOn(dialogRefMock, 'afterClosed').and.returnValue(Observable.of(node));
|
||||
spyOn(contentService.folderCreate, 'next');
|
||||
describe('With overrides', () => {
|
||||
|
||||
contentService.folderCreate.subscribe((val) => {
|
||||
expect(val).toBe(node);
|
||||
done();
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestTypeComponent);
|
||||
element = fixture.debugElement.query(By.directive(FolderCreateDirective));
|
||||
dialog = TestBed.get(MatDialog);
|
||||
contentService = TestBed.get(ContentService);
|
||||
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
xit('should emit folderCreate event when input value is not undefined', (done) => {
|
||||
spyOn(dialogRefMock, 'afterClosed').and.returnValue(Observable.of(node));
|
||||
spyOn(contentService.folderCreate, 'next');
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
element.nativeElement.click();
|
||||
contentService.folderCreate.subscribe((val) => {
|
||||
expect(val).toBe(node);
|
||||
done();
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
element.nativeElement.click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not emit folderCreate event when input value is undefined', () => {
|
||||
spyOn(dialogRefMock, 'afterClosed').and.returnValue(Observable.of(null));
|
||||
spyOn(contentService.folderCreate, 'next');
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
element.nativeElement.click();
|
||||
expect(contentService.folderCreate.next).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit success event with node if the folder creation was successful', async(() => {
|
||||
const testNode = <MinimalNodeEntryEntity> {};
|
||||
fixture.detectChanges();
|
||||
|
||||
element.triggerEventHandler('click', event);
|
||||
dialogRefMock.componentInstance.success.next(testNode);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(fixture.componentInstance.successParameter).toBe(testNode);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should open the dialog with the proper title and nodeType', async(() => {
|
||||
fixture.detectChanges();
|
||||
element.triggerEventHandler('click', event);
|
||||
|
||||
expect(dialog.open).toHaveBeenCalledWith(jasmine.any(Function), {
|
||||
data: {
|
||||
parentNodeId: jasmine.any(String),
|
||||
createTitle: 'create-title',
|
||||
nodeType: 'cm:my-litte-pony'
|
||||
},
|
||||
width: jasmine.any(String)
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it('should not emit folderCreate event when input value is undefined', () => {
|
||||
spyOn(dialogRefMock, 'afterClosed').and.returnValue(Observable.of(null));
|
||||
spyOn(contentService.folderCreate, 'next');
|
||||
describe('Without overrides', () => {
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
element.nativeElement.click();
|
||||
expect(contentService.folderCreate.next).not.toHaveBeenCalled();
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
element = fixture.debugElement.query(By.directive(FolderCreateDirective));
|
||||
dialog = TestBed.get(MatDialog);
|
||||
contentService = TestBed.get(ContentService);
|
||||
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
|
||||
});
|
||||
|
||||
it('should open the dialog with the default title and nodeType', async(() => {
|
||||
fixture.detectChanges();
|
||||
element.triggerEventHandler('click', event);
|
||||
|
||||
expect(dialog.open).toHaveBeenCalledWith(jasmine.any(Function), {
|
||||
data: {
|
||||
parentNodeId: jasmine.any(String),
|
||||
createTitle: null,
|
||||
nodeType: 'cm:folder'
|
||||
},
|
||||
width: jasmine.any(String)
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,10 +35,22 @@ export class FolderCreateDirective {
|
||||
@Input('adf-create-folder')
|
||||
parentNodeId: string = DEFAULT_FOLDER_PARENT_ID;
|
||||
|
||||
/** Emitted when the create folder give error for example a folder with same name already exist */
|
||||
/** Title of folder creation dialog. */
|
||||
@Input()
|
||||
title: string = null;
|
||||
|
||||
/** Type of node to create. */
|
||||
@Input()
|
||||
nodeType = 'cm:folder';
|
||||
|
||||
/** Emitted when an error occurs (eg, a folder with same name already exists). */
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
/** Emitted when the folder is created successfully. */
|
||||
@Output()
|
||||
success: EventEmitter<MinimalNodeEntryEntity> = new EventEmitter<MinimalNodeEntryEntity>();
|
||||
|
||||
@HostListener('click', [ '$event' ])
|
||||
onClick(event) {
|
||||
event.preventDefault();
|
||||
@@ -52,10 +64,10 @@ export class FolderCreateDirective {
|
||||
|
||||
private get dialogConfig(): MatDialogConfig {
|
||||
const { DIALOG_WIDTH: width } = FolderCreateDirective;
|
||||
const { parentNodeId } = this;
|
||||
const { parentNodeId, title: createTitle, nodeType } = this;
|
||||
|
||||
return {
|
||||
data: { parentNodeId },
|
||||
data: { parentNodeId, createTitle, nodeType },
|
||||
width: `${width}px`
|
||||
};
|
||||
}
|
||||
@@ -68,6 +80,10 @@ export class FolderCreateDirective {
|
||||
this.error.emit(error);
|
||||
});
|
||||
|
||||
dialogInstance.componentInstance.success.subscribe((node: MinimalNodeEntryEntity) => {
|
||||
this.success.emit(node);
|
||||
});
|
||||
|
||||
dialogInstance.afterClosed().subscribe((node: MinimalNodeEntryEntity) => {
|
||||
if (node) {
|
||||
content.folderCreate.next(node);
|
||||
|
||||
@@ -15,24 +15,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { Component } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatDialog, MatDialogModule } from '@angular/material';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { ContentService, TranslateLoaderService, DirectiveModule } from '@alfresco/adf-core';
|
||||
import { ContentService, setupTestBed, CoreModule } from '@alfresco/adf-core';
|
||||
import { FolderEditDirective } from './folder-edit.directive';
|
||||
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
|
||||
@Component({
|
||||
template: '<div [adf-edit-folder]="folder"></div>'
|
||||
template: '<div [adf-edit-folder]="folder" (success)="success($event)" title="edit-title"></div>'
|
||||
})
|
||||
class TestComponent {
|
||||
folder = {};
|
||||
public successParameter: MinimalNodeEntryEntity = null;
|
||||
|
||||
success(node: MinimalNodeEntryEntity) {
|
||||
this.successParameter = node;
|
||||
}
|
||||
}
|
||||
|
||||
describe('FolderEditDirective', () => {
|
||||
@@ -48,33 +51,17 @@ describe('FolderEditDirective', () => {
|
||||
preventDefault: () => null
|
||||
};
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
CoreModule.forRoot()
|
||||
],
|
||||
declarations: [
|
||||
TestComponent,
|
||||
FolderEditDirective
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
MatDialogModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
DirectiveModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderService
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [
|
||||
TestComponent,
|
||||
FolderEditDirective
|
||||
]
|
||||
,
|
||||
providers: [
|
||||
ContentService
|
||||
]
|
||||
});
|
||||
|
||||
TestBed.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
element = fixture.debugElement.query(By.directive(FolderEditDirective));
|
||||
dialog = TestBed.get(MatDialog);
|
||||
@@ -85,7 +72,11 @@ describe('FolderEditDirective', () => {
|
||||
node = { entry: { id: 'folderId' } };
|
||||
|
||||
dialogRefMock = {
|
||||
afterClosed: val => Observable.of(val)
|
||||
afterClosed: val => Observable.of(val),
|
||||
componentInstance: {
|
||||
error: new Subject<any>(),
|
||||
success: new Subject<MinimalNodeEntryEntity>()
|
||||
}
|
||||
};
|
||||
|
||||
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
|
||||
@@ -114,4 +105,29 @@ describe('FolderEditDirective', () => {
|
||||
expect(contentService.folderEdit.next).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit success event with node if the folder creation was successful', async(() => {
|
||||
const testNode = <MinimalNodeEntryEntity> {};
|
||||
fixture.detectChanges();
|
||||
|
||||
element.triggerEventHandler('click', event);
|
||||
dialogRefMock.componentInstance.success.next(testNode);
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(fixture.componentInstance.successParameter).toBe(testNode);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should open the dialog with the proper title', async(() => {
|
||||
fixture.detectChanges();
|
||||
element.triggerEventHandler('click', event);
|
||||
|
||||
expect(dialog.open).toHaveBeenCalledWith(jasmine.any(Function), {
|
||||
data: {
|
||||
folder: jasmine.any(Object),
|
||||
editTitle: 'edit-title'
|
||||
},
|
||||
width: jasmine.any(String)
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -35,10 +35,18 @@ export class FolderEditDirective {
|
||||
@Input('adf-edit-folder')
|
||||
folder: MinimalNodeEntryEntity;
|
||||
|
||||
/** Emitted when the edit/create folder give error for example a folder with same name already exist */
|
||||
/** Emitted when an error occurs (eg, a folder with same name already exists). */
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
/** Title of folder edit dialog. */
|
||||
@Input()
|
||||
title: string = null;
|
||||
|
||||
/** Emitted when the folder has been edited successfully. */
|
||||
@Output()
|
||||
success: EventEmitter<MinimalNodeEntryEntity> = new EventEmitter<MinimalNodeEntryEntity>();
|
||||
|
||||
@HostListener('click', [ '$event' ])
|
||||
onClick(event) {
|
||||
event.preventDefault();
|
||||
@@ -58,7 +66,10 @@ export class FolderEditDirective {
|
||||
const { folder } = this;
|
||||
|
||||
return {
|
||||
data: { folder },
|
||||
data: {
|
||||
folder,
|
||||
editTitle: this.title
|
||||
},
|
||||
width: `${width}px`
|
||||
};
|
||||
}
|
||||
@@ -71,6 +82,10 @@ export class FolderEditDirective {
|
||||
this.error.emit(error);
|
||||
});
|
||||
|
||||
dialogInstance.componentInstance.success.subscribe((node: MinimalNodeEntryEntity) => {
|
||||
this.success.emit(node);
|
||||
});
|
||||
|
||||
dialogInstance.afterClosed().subscribe((node: MinimalNodeEntryEntity) => {
|
||||
if (node) {
|
||||
content.folderEdit.next(node);
|
||||
|
||||
@@ -6,7 +6,12 @@
|
||||
"DOWNLOAD": "Herunterladen",
|
||||
"UPLOAD": {
|
||||
"TITLE": "Neue Version hochladen",
|
||||
"TOOLTIP": "Einschränkung: Sie müssen eine Datei mit dem gleichen Namen hochladen, wenn Sie eine neue Version der Datei erstellen möchten."
|
||||
"TOOLTIP": "Einschränkung: Sie müssen eine Datei mit dem gleichen Namen hochladen, wenn Sie eine neue Version der Datei erstellen möchten.",
|
||||
"MAJOR": "bedeutende Änderungen (2.0)",
|
||||
"MINOR": "kleine Änderungen (1.1)",
|
||||
"COMMENT": "Kommentar abgeben",
|
||||
"ADD": "Neue Version hinzufügen",
|
||||
"CANCEL": "Abbrechen"
|
||||
}
|
||||
},
|
||||
"CONFIRM_DELETE": {
|
||||
@@ -72,7 +77,7 @@
|
||||
"SELECT_LOCATION": "Speicherort auswählen"
|
||||
},
|
||||
"OPERATION": {
|
||||
"SUCCES": {
|
||||
"SUCCESS": {
|
||||
"CONTENT": {
|
||||
"COPY": "Kopieren erfolgreich",
|
||||
"MOVE": "Verschieben erfolgreich"
|
||||
@@ -155,6 +160,13 @@
|
||||
},
|
||||
"SEARCH": {
|
||||
"CONTROL": {},
|
||||
"BUTTON": {
|
||||
"TOOLTIP": "Suchen",
|
||||
"ARIA-LABEL": "Suchschaltfläche"
|
||||
},
|
||||
"INPUT": {
|
||||
"ARIA-LABEL": "Sucheingabe"
|
||||
},
|
||||
"RESULTS": {
|
||||
"SUMMARY": "{{numResults}} Ergebnis gefunden für {{searchTerm}}",
|
||||
"NONE": "Keine Ergebnisse gefunden für {{searchTerm}}",
|
||||
@@ -165,6 +177,29 @@
|
||||
"MODIFIED_AT": "Bearbeitet"
|
||||
}
|
||||
},
|
||||
"FILTER": {
|
||||
"ACTIONS": {
|
||||
"CLEAR": "Löschen",
|
||||
"APPLY": "Anwenden",
|
||||
"CLEAR-ALL": "Alle löschen",
|
||||
"SHOW-MORE": "Mehr anzeigen",
|
||||
"SHOW-LESS": "Weniger anzeigen",
|
||||
"FILTER-CATEGORY": "Kategorie filtern"
|
||||
},
|
||||
"RANGE": {
|
||||
"FROM": "Von",
|
||||
"TO": "Bis",
|
||||
"FROM-DATE": "Von",
|
||||
"TO-DATE": "Bis"
|
||||
},
|
||||
"VALIDATION": {
|
||||
"REQUIRED-VALUE": "Erforderlicher Wert",
|
||||
"NO-DAYS": "Keine Tage ausgewählt.",
|
||||
"INVALID-FORMAT": "Unzulässiges Format",
|
||||
"INVALID-DATE": "Unzulässiges Datum. Das Datum muss das Format '{{ requiredFormat }}' haben.",
|
||||
"BEYOND-MAX-DATE": "Das Datum liegt nach dem maximal zulässigen Datum."
|
||||
}
|
||||
},
|
||||
"ICONS": {
|
||||
"ft_ic_raster_image": "Bilddatei",
|
||||
"ft_ic_pdf": "PDF-Dokument",
|
||||
@@ -235,7 +270,20 @@
|
||||
"INHERITED": "Geerbt",
|
||||
"AUTHORITY_ID": "Autoritäts-ID",
|
||||
"ROLE": "Rolle",
|
||||
"LOCALLY_SET": "Lokal festgelegt"
|
||||
"LOCALLY_SET": "Lokal festgelegt",
|
||||
"NO_PERMISSIONS": "Keine Berechtigungen"
|
||||
},
|
||||
"ADD-PERMISSION": {
|
||||
"SEARCH": "Suchen",
|
||||
"TYPE-MESSAGE": "Geben Sie etwas ein, um nach Gruppen oder Personen zu suchen",
|
||||
"NO-RESULT": "Für diese Suchanfrage wurden keine Ergebnisse gefunden",
|
||||
"ADD-ACTION": "Hinzufügen",
|
||||
"CLOSE-ACTION": "Schließen",
|
||||
"BASE-DIALOG-TITLE": "Es wird nach Gruppen oder Personen gesucht, die hinzugefügt werden können...",
|
||||
"EVERYONE": "JEDER"
|
||||
},
|
||||
"ERROR": {
|
||||
"DUPLICATE-PERMISSION": "Mindestens eine der von Ihnen bestimmten Berechtigungen ist bereits vorhanden: {{list}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user