diff options
author | Benaka <diosmosis@users.noreply.github.com> | 2017-10-16 07:54:55 +0300 |
---|---|---|
committer | Matthieu Aubry <mattab@users.noreply.github.com> | 2017-10-16 07:54:55 +0300 |
commit | d4e57274c765698e82e476de4ad6d1a81b65cc91 (patch) | |
tree | 3a36a9eb3d4e58d19123aa352908b60906c48115 /plugins/CoreConsole | |
parent | c1422b533fef97b63b434befe57856ee348c1e46 (diff) |
Convert period selector to angular & allow plugins to add periods to the frontend (#11873)
* Add generate:angular-component command to generate an angular component.
* Do not modify Date prototype.
* Move period selector code from calendar.js to new angular directive (just move, no refactoring).
* Extract date picker code from period selector code and put into new directive.
* Extract range picking code into separate component than period selector.
* Extract single period calendar to separate component & extract period specific functionality to new extendable periods service.
* Fixing regressions in period selector behavior.
* Move bulk of period selector code from directive to controller, & fix variable name in date range picker template.
* Fix issue w/ yesterday date value, remove need to give period selector directive translations and make sure periods can be extended in the frontend.
* Make sure period selector still works outside of an angular routing context (ie, in embedded dashboard).
* In period selector UI test, hide ajaxLoadingCalendar using CSS since it is now managed by angular.
* Make sure selected period highlighting changes immediately after selecting, even if loading a new page.
* Put period selector top level element ID & classes on correct elements to ensure certain styles work properly.
* Make sure selected period text changes immediately after selecing period, even if loading a new page or changing the URL.
* Make sure range start/end changes immediately when a period is selected & selected period date range stops being highlighted immediately when a range period is selected, even if loading a new page.
* Updating expected screenshots.
* Updating screenshots.
* Assorted fixes for period selector refactor.
- Filter out invalid period labels (can happen if INI config for allowed periods is incorrect).
- When determining display text for range, don't try to format the startRangeDate/endRangeDate vars, they're both strings.
- Use correct selector when closing period selector.
* Set global piwik date/period values on location change, outside of period selector component.
* Do not skip parsing date if it does not start with an int (since the JS can handle today/yesterday/now).
* Assorted fixes for period selector refactor:
- use $onChanges instead of watches in datepicker (watches get triggered every time, $onChanges doesn't)
- don't use arrays for selected/highlighted dates (for some weird reason, changing one of these arrays results in angular thinking it changes 3 times instead of once)
- don't redraw on triggered mouseover events (something triggers mouseover when a date is selected, probably jquery datepicker)
- draw after a setTimeout when a date is selected so our drawing occurs after jquery datepicker draws
* Achieving smoother rendering for period selector by removing click handlers jquery datepicker adds.
Also fixed bug where selecting the current period type reset the view date for the date picker.
* Bound range date in period selector by piwik min/max date, so inferred dates will always be within allowed pickable dates in picker.
* Removing ES6 used by accident + fix for issue when switching from non-year to year period (ui-datepicker-current-day class does not get removed).
* Fix for angularjs one way binding quirk: initial property value is set before $onInit not during construction.
* Avoid an exception when a date input in the date range picker is empty.
* Split up change/keyup event to solve strange race condition in IE 10 on browserstack.
* Change period selector "click again" tooltip to "double click".
* Remove tabindexes > 1 so period selector control can be tabbed through.
* Show visual cue for invalid dates in date range picker.
* Only hide period option tooltip if period is active period, not if period is selected period.
* In period selector, disable apply button if range is invalid. Also fix case when \$.datepicker.parseDate returns null instead of throwing.
Diffstat (limited to 'plugins/CoreConsole')
4 files changed, 150 insertions, 56 deletions
diff --git a/plugins/CoreConsole/Commands/GenerateAngularComponent.php b/plugins/CoreConsole/Commands/GenerateAngularComponent.php new file mode 100644 index 0000000000..6342b78240 --- /dev/null +++ b/plugins/CoreConsole/Commands/GenerateAngularComponent.php @@ -0,0 +1,74 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Plugins\CoreConsole\Commands; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class GenerateAngularComponent extends GenerateAngularConstructBase +{ + protected function configure() + { + $this->setName('generate:angular-component') + ->setDescription('Generates a template for an AngularJS component') + ->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin') + ->addOption('component', null, InputOption::VALUE_REQUIRED, 'The name of the component you want to create.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $pluginName = $this->getPluginName($input, $output); + $component = $this->getConstructName($input, $output, $optionName = 'component', $constructType = 'component'); + $pluginPath = $this->getPluginPath($pluginName); + + $componentLower = $this->getSnakeCaseName($component); + + $targetDir = $pluginPath . '/angularjs/' . $componentLower; + + if (is_dir($targetDir) || file_exists($targetDir)) { + throw new \Exception('The AngularJS component ' . $componentLower . ' already exists in plugin ' + . $pluginName); + } + + $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; + $replace = array( + 'ExamplePlugin' => $pluginName, + 'example-component' => $componentLower, + 'componentClass' => lcfirst($component), + 'componentAs' => lcfirst($component), + 'Component' => $component, + ); + + $componentPath = '/angularjs/example-component'; + + $whitelistFiles = array( + '/angularjs', + $componentPath, + $componentPath . '/example-component.component.html', + $componentPath . '/example-component.component.js', + $componentPath . '/example-component.component.less', + ); + + $this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles); + + $replacedBasePath = '/angularjs/' . $componentLower . '/' . $componentLower; + $js1 = $replacedBasePath . '.component.js'; + $less1 = $replacedBasePath . '.component.less'; + + $this->writeSuccessMessage($output, array( + sprintf('AngularJS directive "%s" for plugin "%s" in "%s" generated', $component, $pluginName, $targetDir), + sprintf('In <comment>%1$s/%2$s.php</comment> you should now require the JS files', $pluginPath, $pluginName), + sprintf('<comment>%1$s%2$s</comment>', $pluginPath, $js1), + sprintf('and the less file <comment>%1$s%2$s</comment>.', $pluginPath, $less1), + 'If you are not familiar with this have a look at <comment>http://developer.piwik.org/guides/working-with-piwiks-ui</comment>' + )); + } +}
\ No newline at end of file diff --git a/plugins/CoreConsole/Commands/GenerateAngularConstructBase.php b/plugins/CoreConsole/Commands/GenerateAngularConstructBase.php new file mode 100644 index 0000000000..41d8af385b --- /dev/null +++ b/plugins/CoreConsole/Commands/GenerateAngularConstructBase.php @@ -0,0 +1,72 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Plugins\CoreConsole\Commands; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +abstract class GenerateAngularConstructBase extends GeneratePluginBase +{ + /** + * Convert MyComponentName => my-component-name + * @param string $directiveCamelCase + * @return string + */ + protected function getSnakeCaseName($camelCase) + { + return strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $camelCase)); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @param string $optionName the name of the option to use. + * @param string $constructType 'directive', 'component', etc. + * @return string + * @throws \RuntimeException + */ + protected function getConstructName(InputInterface $input, OutputInterface $output, $optionName, $constructType) + { + $testname = $input->getOption($optionName); + + $validate = function ($testname) use ($constructType) { + if (empty($testname)) { + throw new \InvalidArgumentException("You have to enter a name for the $constructType"); + } + + if (!ctype_alnum($testname)) { + throw new \InvalidArgumentException("Only alphanumeric characters are allowed as a $constructType " + . "name. Use CamelCase if the name of your $constructType contains multiple words."); + } + + return $testname; + }; + + if (empty($testname)) { + $dialog = $this->getHelperSet()->get('dialog'); + $testname = $dialog->askAndValidate($output, "Enter the name of the $constructType you want to create: ", + $validate); + } else { + $validate($testname); + } + + $testname = ucfirst($testname); + + return $testname; + } + + protected function getPluginName(InputInterface $input, OutputInterface $output) + { + $pluginNames = $this->getPluginNames(); + $invalidName = 'You have to enter the name of an existing plugin'; + + return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName); + } +}
\ No newline at end of file diff --git a/plugins/CoreConsole/Commands/GenerateAngularDirective.php b/plugins/CoreConsole/Commands/GenerateAngularDirective.php index afe0ce5581..27fb7a65e0 100644 --- a/plugins/CoreConsole/Commands/GenerateAngularDirective.php +++ b/plugins/CoreConsole/Commands/GenerateAngularDirective.php @@ -15,7 +15,7 @@ use Symfony\Component\Console\Output\OutputInterface; /** */ -class GenerateAngularDirective extends GeneratePluginBase +class GenerateAngularDirective extends GenerateAngularConstructBase { protected function configure() { @@ -28,10 +28,10 @@ class GenerateAngularDirective extends GeneratePluginBase protected function execute(InputInterface $input, OutputInterface $output) { $pluginName = $this->getPluginName($input, $output); - $directive = $this->getDirectiveName($input, $output); + $directive = $this->getConstructName($input, $output, $optionName = 'directive', $constructType = 'directive'); $pluginPath = $this->getPluginPath($pluginName); - $directiveLower = $this->getDirectiveComponentName($directive); + $directiveLower = $this->getSnakeCaseName($directive); $targetDir = $pluginPath . '/angularjs/' . $directiveLower; @@ -75,56 +75,4 @@ class GenerateAngularDirective extends GeneratePluginBase 'If you are not familiar with this have a look at <comment>https://developer.piwik.org/guides/working-with-piwiks-ui</comment>' )); } - - /** - * Convert MyComponentName => my-component-name - * @param string $directiveCamelCase - * @return string - */ - protected function getDirectiveComponentName($directiveCamelCase) - { - return strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $directiveCamelCase)); - } - - /** - * @param InputInterface $input - * @param OutputInterface $output - * @return string - * @throws \RuntimeException - */ - private function getDirectiveName(InputInterface $input, OutputInterface $output) - { - $testname = $input->getOption('directive'); - - $validate = function ($testname) { - if (empty($testname)) { - throw new \InvalidArgumentException('You have to enter a name for the directive'); - } - - if (!ctype_alnum($testname)) { - throw new \InvalidArgumentException('Only alphanumeric characters are allowed as a directive name. Use CamelCase if the name of your directive contains multiple words.'); - } - - return $testname; - }; - - if (empty($testname)) { - $dialog = $this->getHelperSet()->get('dialog'); - $testname = $dialog->askAndValidate($output, 'Enter the name of the directive you want to create: ', $validate); - } else { - $validate($testname); - } - - $testname = ucfirst($testname); - - return $testname; - } - - protected function getPluginName(InputInterface $input, OutputInterface $output) - { - $pluginNames = $this->getPluginNames(); - $invalidName = 'You have to enter the name of an existing plugin'; - - return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName); - } } diff --git a/plugins/CoreConsole/Commands/GeneratePluginBase.php b/plugins/CoreConsole/Commands/GeneratePluginBase.php index 1c49540d60..61db237479 100644 --- a/plugins/CoreConsole/Commands/GeneratePluginBase.php +++ b/plugins/CoreConsole/Commands/GeneratePluginBase.php @@ -348,7 +348,7 @@ abstract class GeneratePluginBase extends ConsoleCommand /** * @param InputInterface $input * @param OutputInterface $output - * @return array + * @return string * @throws \RuntimeException */ protected function askPluginNameAndValidate(InputInterface $input, OutputInterface $output, $pluginNames, $invalidArgumentException) |