diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss index dc2ee648da..3823cdc3d8 100644 --- a/assets/stylesheets/application.css.scss +++ b/assets/stylesheets/application.css.scss @@ -109,6 +109,7 @@ 'pages/qt', 'pages/ramda', 'pages/rdoc', + 'pages/react', 'pages/react_native', 'pages/reactivex', 'pages/redis', diff --git a/assets/stylesheets/pages/_react.scss b/assets/stylesheets/pages/_react.scss new file mode 100644 index 0000000000..6bb32bdfe2 --- /dev/null +++ b/assets/stylesheets/pages/_react.scss @@ -0,0 +1,24 @@ +._react { + @extend %simple; + + .note { + @extend %note; + h4 { + margin-top: .25rem; + margin-bottom: .5rem; + } + } + + .note-orange { + @extend %note-orange; + } + + .note-blue { + @extend %note-blue; + } + + .note-green { + @extend %note-green; + } + +} diff --git a/docs/adding-docs.md b/docs/adding-docs.md index 971b91bece..5051d5ee31 100644 --- a/docs/adding-docs.md +++ b/docs/adding-docs.md @@ -13,7 +13,7 @@ Adding a documentation may look like a daunting task but once you get the hang o 6. Generate the full documentation using the `thor docs:generate [my_doc] --force` command. Additionally, you can use the `--verbose` option to see which files are being created/updated/deleted (useful to see what changed since the last run), and the `--debug` option to see which URLs are being requested and added to the queue (useful to pin down which page adds unwanted URLs to the queue). 7. Start the server, open the app, enable the documentation, and see how everything plays out. 8. Tweak the scraper/filters and repeat 5) and 6) until the pages and metadata are ok. -9. To customize the pages' styling, create an SCSS file in the `assets/stylesheets/pages/` directory and import it in both `application.css.scss` AND `application-dark.css.scss`. Both the file and CSS class should be named `_[type]` where [type] is equal to the scraper's `type` attribute (documentations with the same type share the same custom CSS and JS). Setting the type to `simple` will apply the general styling rules in `assets/stylesheets/pages/_simple.scss`, which can be used for documentations where little to no CSS changes are needed. +9. To customize the pages' styling, create an SCSS file in the `assets/stylesheets/pages/` directory and import it in `application.css.scss`. Both the file and CSS class should be named `_[type]` where [type] is equal to the scraper's `type` attribute (documentations with the same type share the same custom CSS and JS). Setting the type to `simple` will apply the general styling rules in `assets/stylesheets/pages/_simple.scss`, which can be used for documentations where little to no CSS changes are needed. 10. To add syntax highlighting or execute custom JavaScript on the pages, create a file in the `assets/javascripts/views/pages/` directory (take a look at the other files to see how it works). 11. Add the documentation's icon in the `public/icons/docs/[my_doc]/` directory, in both 16x16 and 32x32-pixels formats. The icon spritesheet is automatically generated when you (re)start your local DevDocs instance. 12. Add the documentation's copyright details to `options[:attribution]`. This is the data shown in the table on the [about](https://devdocs.io/about) page, and is ordered alphabetically. Please see an existing scraper for the typesetting. diff --git a/lib/docs/filters/react/clean_html_react_dev.rb b/lib/docs/filters/react/clean_html_react_dev.rb new file mode 100644 index 0000000000..1ad201cd34 --- /dev/null +++ b/lib/docs/filters/react/clean_html_react_dev.rb @@ -0,0 +1,77 @@ +module Docs + class React + class CleanHtmlReactDevFilter < Filter + def call + @doc = at_css('article') + + # Remove breadcrumbs before h1 + css('h1').each do |node| + if node.previous + node.previous.remove + end + end + + remove_selectors = [ + 'div.grid > a', # prev-next links + 'button', # "show more" etc. buttons + 'div.order-last', # code iframe containers + 'div.dark-image', # dark images + 'a[title="Open in CodeSandbox"]', # codesandbox links + ] + css(*remove_selectors).each do |node| + node.remove + end + + # Fix images not loading + css('img').remove_attr('srcset') + + # Remove recipe blocks - TODO transform to outgoing link to docs + css('h4[id^="examples-"]').each do |node| + node.parent.parent.parent.remove + end + + # Transform callout blocks + class_transform = { + '.expandable-callout[class*=yellow]' => 'note note-orange', # pitfalls, experimental + '.expandable-callout[class*=green]' => 'note note-green', # note + '.expandable-callout[class*=gray]' => 'note', # canary + '.bg-card' => 'note', # you will learn + 'details' => 'note note-blue' # deep dive + } + + class_transform.each do |old_class, new_class| + css(old_class).each do |node| + node.set_attribute('class', new_class) + end + end + + # Transform h3 to h4 inside callouts + css('.note h3', '.note h2').each do |node| + new_node = Nokogiri::XML::Node.new('h4', @doc) + new_node.content = node.content + node.replace(new_node) + end + + # Remove styling divs while lifting children + styling_prefixes = %w[ps- mx- my- px- py- mb- sp- rounded-] + selectors = styling_prefixes.map { |prefix| "div[class*=\"#{prefix}\"]:not(.note)" } + css(*selectors, 'div[class=""]', 'div.cm-line').each do |node| + node.before(node.children).remove + end + + # Syntax highlighting + css('pre br').each do |node| + node.replace("\n") + end + css('pre').each do |node| + node['data-language'] = 'jsx' + end + + # Remove styling except for callouts and images + css('*:not([class*=image]):not(.note)').remove_attr('class').remove_attr('style') + + doc + end + end + end +end diff --git a/lib/docs/filters/react/entries_react_dev.rb b/lib/docs/filters/react/entries_react_dev.rb new file mode 100644 index 0000000000..ce6da0e056 --- /dev/null +++ b/lib/docs/filters/react/entries_react_dev.rb @@ -0,0 +1,32 @@ +module Docs + class React + class EntriesReactDevFilter < Docs::EntriesFilter + def get_name + name = at_css('article h1')&.content + + update_canary_copy(name) + end + + def get_type + # Category is the opened category in the sidebar + category = css('a:has(> span.text-link) > div').first&.content + # The grey category in the sidebar + top_category = css('h3:has(~ li a.text-link)') + .last&.content + &.sub(/@.*$/, '') # remove version tag + &.sub(/^./, &:upcase) # capitalize first letter + &.concat(": ") + is_learn_page = path.start_with?('learn/') || slug == 'learn' + prefix = is_learn_page ? 'Learn: ' : top_category || '' + + update_canary_copy(prefix + (category || 'Miscellaneous')) + end + + def update_canary_copy(string) + canary_copy = '- This feature is available in the latest Canary' + + string.sub(canary_copy, ' (Canary)') + end + end + end +end diff --git a/lib/docs/scrapers/react.rb b/lib/docs/scrapers/react.rb index eeececf621..9bc1249cd5 100644 --- a/lib/docs/scrapers/react.rb +++ b/lib/docs/scrapers/react.rb @@ -1,38 +1,59 @@ module Docs class React < UrlScraper + self.name = 'React' - self.type = 'simple' - self.release = '18.2.0' - self.base_url = 'https://reactjs.org/docs/' - self.root_path = 'hello-world.html' + self.type = 'react' self.links = { - home: 'https://reactjs.org/', + home: 'https://react.dev/', code: 'https://github.com/facebook/react' } - html_filters.push 'react/entries', 'react/clean_html' - - options[:skip] = %w( - codebase-overview.html - design-principles.html - how-to-contribute.html - implementation-notes.html - ) - - options[:replace_paths] = { - 'more-about-refs.html' => 'refs-and-the-dom.html', - 'interactivity-and-dynamic-uis.html' => 'state-and-lifecycle.html', - 'working-with-the-browser.html' => 'refs-and-the-dom.html', - 'top-level-api.html' => 'react-api.html', - } - options[:attribution] = <<-HTML © 2013–present Facebook Inc.
Licensed under the Creative Commons Attribution 4.0 International Public License. HTML + version do + self.release = '19' + self.base_url = 'https://react.dev' + self.initial_paths = %w(/reference/react /learn) + html_filters.push 'react/entries_react_dev', 'react/clean_html_react_dev' + + options[:only_patterns] = [/\A\/learn/, /\A\/reference/] + end + + version '18' do + self.release = '18.3.1' + self.base_url = 'https://18.react.dev' + self.initial_paths = %w(/reference/react /learn) + html_filters.push 'react/entries_react_dev', 'react/clean_html_react_dev' + + options[:only_patterns] = [/\A\/learn/, /\A\/reference/] + end + + version '17' do + self.release = '17.0.2' + self.base_url = 'https://17.reactjs.org/docs/' + self.root_path = 'hello-world.html' + html_filters.push 'react/entries', 'react/clean_html' + + options[:skip] = %w( + codebase-overview.html + design-principles.html + how-to-contribute.html + implementation-notes.html + ) + + options[:replace_paths] = { + 'more-about-refs.html' => 'refs-and-the-dom.html', + 'interactivity-and-dynamic-uis.html' => 'state-and-lifecycle.html', + 'working-with-the-browser.html' => 'refs-and-the-dom.html', + 'top-level-api.html' => 'react-api.html', + } + end + def get_latest_version(opts) - doc = fetch_doc('https://reactjs.org/docs/getting-started.html', opts) + doc = fetch_doc('https://react.dev/', opts) doc.at_css('a[href="/versions"]').content.strip[1..-1] end end diff --git a/public/icons/docs/react/16.png b/public/icons/docs/react/16.png index 2d38451711..d24cb4f765 100644 Binary files a/public/icons/docs/react/16.png and b/public/icons/docs/react/16.png differ diff --git a/public/icons/docs/react/16@2x.png b/public/icons/docs/react/16@2x.png index 016a0c3c87..953ae4cc3a 100644 Binary files a/public/icons/docs/react/16@2x.png and b/public/icons/docs/react/16@2x.png differ diff --git a/public/icons/docs/react/SOURCE b/public/icons/docs/react/SOURCE index bfd02fd2f4..e1aa2f5547 100644 --- a/public/icons/docs/react/SOURCE +++ b/public/icons/docs/react/SOURCE @@ -1,2 +1,2 @@ -https://es.m.wikipedia.org/wiki/React#/media/Archivo%3AReact.svg -https://github.com/facebook/react/blob/master/docs/img/logo.svg \ No newline at end of file +https://github.com/reactjs/react.dev/blob/master/public/favicon-16x16.png +https://github.com/reactjs/react.dev/blob/master/public/favicon-32x32.png